0

please view my code. Application is working ok but when i scroll fast of up or down it just crashes.

* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'

Here is cellforrawatindexpath method

- (UITableViewCell *)tableView:(UITableView *)myTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

        UITableViewCell *cell = (UITableViewCell *)[self.messageList dequeueReusableCellWithIdentifier:@"ChatListItem"];
        if (cell == nil) {
            NSArray *nib;
            if ([[[rssOutputData objectAtIndex:indexPath.row] xml_userType] isEqualToString:@"2"])// THIS IS THE LINE WHERE IT CRASHES
 {
                nib = [[NSBundle mainBundle] loadNibNamed:@"OutGoing" owner:self options:nil];
                textLabel =[[UILabel alloc] initWithFrame:CGRectMake(58,12,241,30 )];

                cell.textLabel.textAlignment = NSTextAlignmentLeft;

            }else {
                nib = [[NSBundle mainBundle] loadNibNamed:@"InComing" owner:self options:nil];
                textLabel =[[UILabel alloc] initWithFrame:CGRectMake(21,12,241,30)];
                cell.textLabel.textAlignment = NSTextAlignmentRight;
            }
            cell = (UITableViewCell *)[nib objectAtIndex:0];
        }
        textLabel = (UILabel *)[cell viewWithTag:1];
        userLabel = (UILabel *)[cell viewWithTag:2];
        avatar = (UIImageView *)[cell viewWithTag:3];
        timeLabel = (UILabel *)[cell viewWithTag:4];

        NSString * test=[[rssOutputData objectAtIndex:indexPath.row]xml_msg];
        textLabel.text = test;
        textLabel.numberOfLines = 0; 
        [textLabel sizeToFit];
        [cell addSubview:textLabel];
        userLabel.text = [[rssOutputData objectAtIndex:indexPath.row]xml_username];

        avatar.layer.cornerRadius = 20.0;
        avatar.layer.masksToBounds = YES;
        if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"patient_name"] isEqualToString:[[rssOutputData objectAtIndex:indexPath.row]xml_username]] || [[[NSUserDefaults standardUserDefaults] stringForKey:@"therapist"] isEqualToString:[[rssOutputData objectAtIndex:indexPath.row]xml_username]]) {
            avatar.image = [UIImage imageNamed:@"p.jpg"];
        }else {
            avatar.image = [UIImage imageNamed:@"t.jpg"];
        }
        rectTime = (CGRect){0, textLabel.frame.size.height+5, 241, 11};
        timeLabel = [[UILabel alloc] initWithFrame:rectTime];
        timeLabel.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]];
        timeLabel.text = [[rssOutputData objectAtIndex:indexPath.row]xml_addedTime];
        timeLabel.backgroundColor = [UIColor clearColor];
        timeLabel.textColor = [UIColor lightGrayColor];
        [textLabel addSubview:timeLabel];

        return cell;
    }

actually i am loading data from xml file which is calling after every 5 sec. i will post my parsing code here.

- (void) Parse{
        previusCount = rssOutputData.count;
        rssOutputData = [[NSMutableArray alloc]init];
        NSString *post =[[NSString alloc] initWithFormat:@"https://messages_%@.xml",[[NSUserDefaults standardUserDefaults] stringForKey:@"xmls_id"]];
           dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0),^{

            NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:post]];
            xmlParserObject =[[NSXMLParser alloc]initWithData:xmlData];
            [xmlParserObject setDelegate:self];
            [xmlParserObject parse];

            dispatch_async(dispatch_get_main_queue(), ^{
                [messageList reloadData];
            });
        });
}

and here is the NSTIMER from ViewDidLoad

 timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target: self selector: @selector(Parse) userInfo: nil repeats: YES];

here is rest of the parsing code

#pragma mark NSXMLParser delegate
//below delegate method is sent by a parser object to provide its delegate when it encounters a start tag
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
    if([elementName isEqualToString:@"message"]){
        xmlStringFileObject =[[XMLChatFile alloc]init];
    } else {
        nodecontent = [[NSMutableString alloc] init];
    }
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    [nodecontent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if([elementName isEqualToString:@"message"]){
        [rssOutputData addObject:xmlStringFileObject];
        xmlStringFileObject = nil;
    }
    else if([elementName isEqualToString:@"added"]){
        xmlStringFileObject.xml_addedTime= nodecontent;
        nodecontent = nil;
    }
    else if([elementName isEqualToString:@"user"]){
        xmlStringFileObject.xml_username= nodecontent;
        nodecontent = nil;
    }
    else if([elementName isEqualToString:@"text"]){
        xmlStringFileObject.xml_msg= nodecontent;
        nodecontent = nil;
    }
    else if([elementName isEqualToString:@"user_type"]){
        xmlStringFileObject.xml_userType= nodecontent;
        nodecontent = nil;
    }
}
Vix Hunk
  • 303
  • 4
  • 17

2 Answers2

0

It looks like your rssOutputData is empty or is lower than the numberOfRows.

See you use rssOutputData and after that you reset it:

// ...
previusCount = rssOutputData.count;
rssOutputData = [[NSMutableArray alloc]init]; 
// ...

and in the code you shared you don't set any values to it again.

J. Lopes
  • 1,336
  • 16
  • 27
  • can you please tell me how can set values to it! will appreciate thanks – Vix Hunk May 03 '16 at 12:49
  • yes actually i am saving its previous count to compare if there is any increment in the count. after saving that rssOutputData.count resetting it to get new values from xml. – Vix Hunk May 03 '16 at 12:59
  • is this [xmlParserObject parse]; returning anything? So looking your code, you have to set to rssOutputData an array of XML objects. If [xmlParserObject parse] has been returning an array of XML objects, you can do: rssOutputData = [[NSMutableArray alloc] initWithArray:[xmlParserObject parse]]; – J. Lopes May 03 '16 at 13:01
  • So where are you resetting rssOutputData? Could you share the code? – J. Lopes May 03 '16 at 13:02
0

Yup, to state the obvious, you're attempting to access an element in one of your arrays which doesn't exist.

Question: if you "enable zombies" in Xcode then re-run your code, does it show you which line is causing the problem ?

Enable zombies in Xcode

From there, you should be able to track down the cause.

You could also try sticking something like this at the top of your cellForRowAtIndexPath function...

if (rssOutputData == nil)
    return;
if (rssOutputData.count <= objectAtIndex:indexPath.row)
    return;

... but I still reckon that this problem is being caused by your UITableView attempting to (quickly) load and display data, which your 5-second function is busy emptying-then-refreshing.

After all, that every-5-seconds function is emptying your array, then waiting to repopulate it using a web service.

Until that web service completes, your UITableView is bound to a data source which has no data in it... this can't be healthy !!

    rssOutputData = [[NSMutableArray alloc]init];
    NSString *post =[[NSString alloc] initWithFormat:@"https://messages_%@.xml",
        [[NSUserDefaults standardUserDefaults] stringForKey:@"xmls_id"]];
   ... load data from this web service...

No wonder it crashes when you're scrolling....

Btw, in your Parse function, where do you set the rssOutputData value to whatever you've just loaded in from your web service...?

Community
  • 1
  • 1
Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159
  • i disabled the Timer code and after that app isnt crashing anymore but if i did that how am i going to refresh xml and messages if it is updated on server! – Vix Hunk May 03 '16 at 13:09
  • See my final sentence: Right now, your Parse function "empties" the array you're binding to... but you don't seem to ever fill it again...? Basically, I don't see where "rssOutputData" is getting set.... – Mike Gledhill May 03 '16 at 13:12
  • i updated my question with rest of the code. Please view – Vix Hunk May 03 '16 at 13:15
  • I've viewed ! But, my comment remains the same. You have a UITableView showing, say 100 records. Your Parse function empties this array (during which time you can continue scrolling the tableview and it'll crash, as these items no longer exist) and the UITableView's datasource doesn't get any data again until that web service completes. To see what I mean, add [messageList reloadData] immediately after the Parse line where you empty your "rssOutputData" array. Then you'll see the REAL amount of data which your table is able to view at any moment in time.... – Mike Gledhill May 03 '16 at 13:19
  • yes exactly you are right that is what actually happening most of the times or should i say every time crashes when i scroll. so what can i do to prevent it?? how can i update entries if i dont use timer!! Thanks for you time. – Vix Hunk May 03 '16 at 13:22
  • You basically need to use a NSManagedObjects as your data source. It's ugly, but it does all the heavy lifting for you. And if you bind your table to a CoreData database, then go and download mogenerator. It'll save your sanity. Just. – Mike Gledhill May 03 '16 at 13:28
  • Or, rather than refreshing every 5 seconds, add a "scroll to refresh" control. – Mike Gledhill May 03 '16 at 13:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/110912/discussion-between-mike-gledhill-and-vix-hunk). – Mike Gledhill May 03 '16 at 13:38