2012-01-22 2 views
4

RSS-Reader를 빌드하고 탐색 모음의 오른쪽 모서리에 새로 고침 단추를 넣습니다. 그것은 잘 작동하고 나는 어떤 충돌도 얻지 않는다. 그러나 스크롤하는 동안 새로 고침 단추를 누르면 응용 프로그램이 충돌합니다. 그리고 나는 그 문제가 어디에 있는지 전혀 모른다. 내가 프로젝트를 분석하지만 아무것도 찾을 수 없습니다 ...빈 배열 충돌 경계를 초과하는 인덱스 '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; 
} 

과 유사하게 당신이 [[self rssParser]rssItems]을 참조하는 다른 모든 tableview에 위임 방법의 예를 들면 다음과 같습니다.

+0

정확히 어떻게 할 수 있습니까? 나는 정말로 확신하지 못한다 – AmiiQo

+0

편집 된 응답, 죄송합니다 일단 입력 한 다음 브라우저가 추락했습니다. – NJones

+0

정말 고마워, 널 사랑해. P! – AmiiQo

1

가 작성되고 전에, 당신은 스크롤과 동시에 새로 고침하는 동안, 당신의 데이터 소스가 비워되고 있음을 수 있습니다. 그래서, 당신의 tableview 5 행이 있다고 생각하는 동안 귀하의 소스가 어디서나 그들을 다운로드하고 있기 때문에 귀하의 데이터 소스 5 항목이 없습니다. 다섯 번째 항목을 쿼리하면 아무 것도없고 응용 프로그램이 넘어집니다.

편집

내가 옳았다. 새로 고침 코드는 startProcess이며 배열을 채우는 데 사용하는 배열을 비운 다음 한 번에 하나의 항목을 추가하고 백그라운드 대기열에서이 작업을 수행하므로 비동기가됩니다.

이 해결 방법은 백그라운드 큐의 중간 배열에 새 항목을 쓰고 프로세스가 끝나면 현재 rssItems를이 배열로 바꾸고 테이블 뷰를 다시로드하는 것입니다.

+0

그 말이된다. 나는 초보자이기 때문에 어떻게 작동하도록 코드를 바꿀 수 있는지 알고 있습니까? – AmiiQo

+0

이것은 가져 오기 코드 구현에 따라 다릅니다. – Abizern

0

테이블 뷰를 다시로드하는 동안 확인하십시오. 스크롤하면 테이블 뷰가 자동으로 호출됩니다. 그래서 셀에 내용을 할당하기 전에 배열에 값이 있는지 확인하십시오. 배열 수를 검사하는 것이 0보다 greater이고이 줄을 씁니다.새로 고침 호출 된 메소드의 시작 부분에 코드 줄을 추가 내 경우

0

일했다 : 당신이 마지막에 다시있는 tableView를 설정해야합니다 물론

tableView.scrollEnabled = NO; 

:

tableView.scrollEnabled = YES; 
+0

새로 고침을 누른 후 스크롤하면 유용합니다. 하지만 스크롤 한 다음 새로 고침을하면 어떻게됩니까? – AmiiQo

+0

음 ... 내 코드는 실제로 좀 더 복잡했습니다. 내 tableView (activityIndicator) 맨 위에 마스크를 표시, 내 테이블 위에 스크롤, scrollEnabled NO로 설정하고 마지막으로 내 tableView 다시로드하십시오. – Beppe