2012-01-22 4 views
4

Я создаю RSS-Reader и поставлю кнопку refreshbutton в правом углу панели навигации. Он отлично работает, и я не получаю никаких сбоев. Но если при нажатии кнопки обновления во время прокрутки приложение выйдет из строя. И я понятия не имею, где проблема. Я проанализировал проект, но он не мог найти что-нибудь ...Индекс '5' за пределами ошибки пустого массива

Так вот ошибка я получаю:

2012-01-22 16:36:48.205 GYSA[712:707] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds for empty array' 
*** First throw call stack: 
(0x37adb8bf 0x315c11e5 0x37a24b6b 0x7913 0x34ef39cb 0x34ef2aa9 0x34ef2233 0x34e96d4b 0x37a3a22b 0x33231381 0x33230f99 0x3323511b 0x33234e57 0x3325c6f1 0x3327f4c5 0x3327f379 0x37249f93 0x3747b891 0x37aa4f43 0x37aaf553 0x37aaf4f5 0x37aae343 0x37a314dd 0x37a313a5 0x375affcd 0x34ec1743 0x2ac9 0x2a54) 
terminate called throwing an exception(gdb) 

И вот мой код:

#import "RssFunViewController.h" 
#import "BlogRssParser.h" 
#import "BlogRss.h" 

@implementation RssFunViewController 

@synthesize rssParser = _rssParser; 
@synthesize tableView = _tableView; 
@synthesize appDelegate = _appDelegate; 
@synthesize toolbar = _toolbar; 

-(void)toolbarInit{ 
    UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] 
            initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 
            target:self action:@selector(reloadRss)]; 
    refreshButton.enabled = YES; 
    self.navigationItem.rightBarButtonItem = refreshButton; 
    [refreshButton release]; 
    UIImage *image = [UIImage imageNamed: @"navigationbar.png"]; 
    UIImageView *imageview = [[UIImageView alloc] initWithImage: image]; 

    UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithCustomView: imageview]; 
    self.navigationItem.leftBarButtonItem = button; 
    [imageview release]; 
    [button release]; 
} 


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. 
- (void)viewDidLoad { 

    [super viewDidLoad]; 
    self.view.autoresizesSubviews = YES; 
    self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 
    [self toolbarInit]; 
    _rssParser = [[BlogRssParser alloc]init]; 
    self.rssParser.delegate = self; 
    [[self rssParser]startProcess]; 
} 

-(void)reloadRss{ 
    [self toggleToolBarButtons:NO]; 
    [[self rssParser]startProcess]; 
} 

-(void)toggleToolBarButtons:(BOOL)newState{ 
    NSArray *toolbarItems = self.toolbar.items; 
    for (UIBarButtonItem *item in toolbarItems){ 
     item.enabled = newState; 
    } 
} 

//Delegate method for blog parser will get fired when the process is completed 
- (void)processCompleted{ 
    //reload the table view 
    [self toggleToolBarButtons:YES]; 
    [[self tableView]reloadData]; 
} 

-(void)processHasErrors{ 
    //Might be due to Internet 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Achtung!" message:@"Leider ist es im Moment nicht möglich eine Verbindung zum Internet herzustellen. Ohne Internetverbindung ist die App nur in beschränktem Umfang nutzbar!" 
                delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil]; 
    [alert show]; 
    [alert release]; 
    [self toggleToolBarButtons:YES]; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 
    return [[[self rssParser]rssItems]count]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"rssItemCell"]; 
    if(nil == cell){ 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"rssItemCell"]autorelease]; 
    } 
    cell.textLabel.text = [[[[self rssParser]rssItems]objectAtIndex:indexPath.row]title]; 
    cell.detailTextLabel.text = [[[[self rssParser]rssItems]objectAtIndex:indexPath.row]description]; 
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 
    return cell; 
} 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    [[self appDelegate] setCurrentlySelectedBlogItem:[[[self rssParser]rssItems]objectAtIndex:indexPath.row]]; 
    [self.appDelegate loadNewsDetails]; 
    [_tableView deselectRowAtIndexPath:indexPath animated: YES]; 
} 

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{ 
    // Return YES for supported orientations 
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); 
} 

- (void)dealloc { 
    [_appDelegate release]; 
    [_toolbar release]; 
    [_tableView release]; 
    [_rssParser release]; 
    [super dealloc]; 
} 

@end 

Я нашел строку кода, что вызывает проблему:

cell.textLabel.text = [[[[self rssParser]rssItems]objectAtIndex:indexPath.row]title]; 
cell.detailTextLabel.text = [[[[self rssParser]rssItems]objectAtIndex:indexPath.row]description]; 

Если я удалю эти коды, я не могу воспроизвести ошибку. Но они необходимы для RSS-канала, как вы можете себе представить :).

Любые решения?

Вот выборка код:

#import "BlogRssParser.h" 
#import "BlogRss.h" 

@implementation BlogRssParser 

@synthesize currentItem = _currentItem; 
@synthesize currentItemValue = _currentItemValue; 
@synthesize rssItems = _rssItems; 
@synthesize delegate = _delegate; 
@synthesize retrieverQueue = _retrieverQueue; 


- (id)init{ 
    self = [super init]; 
    if(self){ 
     _rssItems = [[NSMutableArray alloc]init]; 
    } 
    return self; 
} 

- (NSOperationQueue *)retrieverQueue { 
    if(nil == _retrieverQueue) { 
     _retrieverQueue = [[NSOperationQueue alloc] init]; 
     _retrieverQueue.maxConcurrentOperationCount = 1; 
    } 
    return _retrieverQueue; 
} 

- (void)startProcess{ 
    SEL method = @selector(fetchAndParseRss); 
    [[self rssItems] removeAllObjects]; 
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self 
                    selector:method 
                     object:nil]; 
    [self.retrieverQueue addOperation:op]; 
    [op release]; 
} 

-(BOOL)fetchAndParseRss{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; 

    //To suppress the leak in NSXMLParser 
    [[NSURLCache sharedURLCache] setMemoryCapacity:0]; 
    [[NSURLCache sharedURLCache] setDiskCapacity:0]; 

    BOOL success = NO; 
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url]; 
    [parser setDelegate:self]; 
    [parser setShouldProcessNamespaces:YES]; 
    [parser setShouldReportNamespacePrefixes:YES]; 
    [parser setShouldResolveExternalEntities:NO]; 
    success = [parser parse]; 
    [parser release]; 
    [pool drain]; 
    return success; 
} 

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI 
qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict{ 
    if(nil != qualifiedName){ 
     elementName = qualifiedName; 
    } 
    if ([elementName isEqualToString:@"item"]) { 
     self.currentItem = [[[BlogRss alloc]init]autorelease]; 
    }else if ([elementName isEqualToString:@"media:thumbnail"]) { 
     self.currentItem.mediaUrl = [attributeDict valueForKey:@"url"]; 
    } else if([elementName isEqualToString:@"title"] || 
       [elementName isEqualToString:@"description"] || 
       [elementName isEqualToString:@"link"] || 
       [elementName isEqualToString:@"guid"] || 
       [elementName isEqualToString:@"pubDate"]) { 
     self.currentItemValue = [NSMutableString string]; 
    } else { 
     self.currentItemValue = nil; 
    } 
} 

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { 
    if(nil != qName){ 
     elementName = qName; 
    } 
    if([elementName isEqualToString:@"title"]){ 
     self.currentItem.title = self.currentItemValue; 
    }else if([elementName isEqualToString:@"description"]){ 
     self.currentItem.description = self.currentItemValue; 
    }else if([elementName isEqualToString:@"link"]){ 
     self.currentItem.linkUrl = self.currentItemValue; 
    }else if([elementName isEqualToString:@"guid"]){ 
     self.currentItem.guidUrl = self.currentItemValue; 
    }else if([elementName isEqualToString:@"pubDate"]){ 
     NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 
     [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"]; 
     self.currentItem.pubDate = [formatter dateFromString:self.currentItemValue]; 
     [formatter release]; 
    }else if([elementName isEqualToString:@"item"]){ 
     [[self rssItems] addObject:self.currentItem]; 
    } 
} 

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 
    if(nil != self.currentItemValue){ 
     [self.currentItemValue appendString:string]; 
    } 
} 

- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock{ 
    //Not needed for now 
} 

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{ 
    if(parseError.code != NSXMLParserDelegateAbortedParseError) { 
     [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; 
     [(id)[self delegate] performSelectorOnMainThread:@selector(processHasErrors) 
     withObject:nil 
     waitUntilDone:NO]; 
    } 
} 



- (void)parserDidEndDocument:(NSXMLParser *)parser { 
    [(id)[self delegate] performSelectorOnMainThread:@selector(processCompleted) 
    withObject:nil 
    waitUntilDone:NO]; 
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; 
} 


-(void)dealloc{ 
    self.currentItem = nil; 
    self.currentItemValue = nil; 
    self.delegate = nil; 

    [_rssItems release]; 
    [super dealloc]; 
} 

@end 

ответ

6

Что вы должны делать, это копирование вашего выбранного массива данных в ivar. Затем заполнение таблицы из этого ivar, а затем в processCompleted копирование новых данных в ivar и вызов reloadData. Это приведет к тому, что табличное представление будет находиться в непоследовательном состоянии, которое вы испытываете.

@property (retain, nonatomic) NSArray *sourceArray; 

- (void)processCompleted{ 
    self.sourceArray = [[[[self rssParser]rssItems] copy] autorelease]; 
    [self toggleToolBarButtons:YES]; 
    [[self tableView]reloadData]; 
} 

А затем, когда заполнение табличного вида относится к скопированному массиву. Например:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"rssItemCell"]; 
    if(nil == cell){ 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"rssItemCell"]autorelease]; 
    } 
    cell.textLabel.text = [[self.sourceArray objectAtIndex:indexPath.row]title]; 
    cell.detailTextLabel.text = [[self.sourceArray objectAtIndex:indexPath.row]description]; 
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 
    return cell; 
} 

И точно так же в любом другом методе TableView делегата, где вы ссылаетесь [[self rssParser]rssItems].

+0

Как именно вы это сделаете? Я действительно не уверен – AmiiQo

+0

Отредактированный ответ, извините, если бы он набрал один раз, то мой браузер разбился. – NJones

+0

Большое вам спасибо, я тебя люблю: P! – AmiiQo

1

Это может быть, что в то время как вы прокрутки и обновления в то же время, ваш источник данных опорожнения, до его заполнения. Итак, в то время как ваш tableview считает, что он имеет 5 строк, ваш источник данных не имеет 5 элементов, потому что вы загружаете их из источника, где бы вы ни находились. И когда он запрашивает пятый элемент, там ничего нет, и ваше приложение падает.

Редактировать

я был прав. Ваш код обновления вызывает startProcess, который освобождает массив, который вы используете для заполнения массива, и затем вы добавляете к нему элемент за раз, и вы делаете это в фоновом режиме, поэтому он, вероятно, асинхронен.

Решение состоит в том, чтобы записать ваши новые элементы в промежуточный массив в вашей фоновой очереди, а когда процесс завершится, замените текущие rssItems на этот массив и перезагрузите таблицу.

+0

Это имеет смысл. Я новичок, так что вы знаете, как я могу изменить код, чтобы он работал? – AmiiQo

+0

Это зависит от вашей реализации вашего кода. – Abizern

0

Проверка при перегрузке стола. Когда вы прокручиваете перезагрузку, табличное представление автоматически вызывается. Поэтому проверьте, имеет ли массив значения в нем, прежде чем назначать его содержимое ячейке. Проверьте, что количество массивов больше, чем 0, и затем записывайте эти строки.

0

В моем случае добавления этой строки кода в начале метода, называемого для обновления работали:

tableView.scrollEnabled = NO; 

Конечно, вы должны установить Tableview снова в конце:

tableView.scrollEnabled = YES; 
+0

Это просто полезно, если я прокручиваю после того, как я нажал обновление. Но что, если я прокручу и ТОГДА нажмите обновление? – AmiiQo

+0

Ммм ... мой код был на самом деле немного сложнее. Я показываю маску поверх моего tableView (с помощью ActivityIndicator), прокручиваю таблицу вверху, устанавливаю scrollEnabled в НЕТ и, наконец, перезаряжаю таблицу TableView. – Beppe

Смежные вопросы