我有一个类PictureDownloader,用于从服务器异步加载图像。它将自己指定为NSURLConnection的委托,因此由NSURLConnection保留。我在DetailViewController中创建了几个PictureDownloader来获取相应的图像,因此DetailViewController是每个PictureDownloader的委托。 当用户离开DetailViewController时,所有剩余的下载都会被取消,但有时看起来似乎是这样,PictureDownloader在取消连接之前已完成加载图像(connectionDidFinishedLoading),但DetailViewController不再存在(但是PictureDownloader会这样做,因为它是由NSURLConnection保留的,所以调用
[self.delegate didLoadPictureWithID:self.ID];
在PictureDownloader内部将提供EXC_BAD_ACCESS或有时“无法识别的选择器发送到实例”。
以下是源代码的相关部分:
在DetailViewController中创建PictureDownloader
- (void)startPictureDownload:(Picture *)pic withPictureId:(NSString *)pId forID:(int)ID
{
PictureDownloader *downloader = [self.downloadsInProgress objectForKey:[NSNumber numberWithInt:ID]];
if(!downloader)
{
downloader = [[PictureDownloader alloc] init];
downloader.picture = pic;
downloader.pictureId = pId;
downloader.ID = ID;
downloader.delegate = self;
[self.downloadsInProgress setObject:downloader forKey:[NSNumber numberWithInt:ID]];
[downloader startDownload];
[downloader release];
}
}
取消下载(当DetailViewController返回概述时调用)
- (void)cancelAllDownloads
{
[self.downloadsInProgress enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
[obj cancelDownload];
}];
}
当PictureDownloader完成加载时调用的委托方法
- (void)didLoadPictureWithID:(int)dID;
{
PictureDownloader *downloader = [self.downloadsInProgress objectForKey:[NSNumber numberWithInt:dID]];
if(downloader)
{
UIImageView *imageView = (UIImageView *)[self.view viewWithTag:dID];
imageView.image = [UIImage imageWithData:downloader.imageData];
[self.downloadsInProgress removeObjectForKey:[NSNumber numberWithInt:dID]];
}
}
PictureDownloader中的cancelDownload方法
- (void)cancelDownload
{
[self.imageConnection cancel];
self.imageConnection = nil;
self.imageData = nil;
}
PictureDownloader内的connectionDidFinishedLoading
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if(self.picture)
{
self.picture.data = self.imageData;
NSError *error = nil;
[self.picture.managedObjectContext save:&error];
}
if(self.delegate != nil && [self.delegate respondsToSelector:@selector(didLoadPictureWithID:)] ) //place of failure
[self.delegate didLoadPictureWithID:self.ID];
self.imageData = nil;
self.imageConnection = nil;
}
有人可以给我一个提示,我该如何处理这个问题?
非常感谢帮助。
答案 0 :(得分:0)
在尝试拨打电话之前,您应该检查是否存在委托对象(最好是方法/选择器)。
例如:
if(self.delegate && [[self.delegate] respondsToSelector:@selector(didLoadPictureWithID:)]) {
...
}
通过这样做,您将确保您不会尝试呼叫不再存在的代理人。有关respondsToSelector方法的更多信息,请参阅NSObject Protocol Reference。
答案 1 :(得分:0)
为了避免这种情况,我通常会在connectionDidFinishLoading:
和其他NSURLConnection委托方法的顶部添加这样的支票:
if (connection != self.imageConnection) return;
作为另一种选择,您可以在cancelAllDownloads
中取消时将每个PictureDownloader上的委托设置为nil。或者您可以在self.delegate = nil
中设置cancelDownload
。
答案 2 :(得分:0)
当你的DetailViewController超出范围 - dealloc - 时,将PictureDownloader的委托属性设置为nil。
你的问题很有趣,因为NSUrlConnection的代表不能以同样的方式设置为nil。例如当您取消分配PictureDownloader时。您所能做的就是取消NSUrlConnection。
NSURLConnection文档说出以下内容:
除非NSURLConnection收到取消消息,否则委托将只接收connectionDidFinishLoading:或connection:didFailWithError:message中的一个,但不会同时接收两者。此外,一旦发送了任何一条消息,代理将不会收到给定NSURLConnection的其他消息。
表示您可以在收到上述消息后验证是否不会回叫该代表。