0

我有一个类 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;
}

有人可以给我一个提示,我该如何处理这个问题?

非常感谢您的帮助。

4

3 回答 3

0

在尝试进行调用之前,您应该检查委托对象(理想情况下是方法/选择器)是否存在。

例如:

if(self.delegate && [[self.delegate] respondsToSelector:@selector(didLoadPictureWithID:)]) {
    ...
}

通过这样做,您将确保您不会尝试调用不再存在的委托。有关 respondsToSelector 方法的更多信息,请参阅NSObject 协议参考

于 2011-03-07T18:18:34.683 回答
0

为了避免这种情况,我通常在connectionDidFinishLoading:其他 NSURLConnection 委托方法的顶部添加这样的检查:

if (connection != self.imageConnection) return;

作为另一种选择,您可以将每个 PictureDownloader 上的委托设置为 nil,因为您在cancelAllDownloads. 或者你可以设置self.delegate = nilcancelDownload.

于 2011-03-07T20:46:32.380 回答
0

当您的 DetailViewController 超出范围 - dealloc - 时,将 PictureDownloader 的委托属性也设置为 nil。

您的问题很有趣,因为 NSUrlConnection 的委托不能以相同的方式设置为 nil。例如,当您的 PictureDownloader 被取消分配时。您所能做的就是取消 NSUrlConnection。

NSURLConnection 文档说以下内容:

除非 NSURLConnection 收到取消消息,否则委托将收到一个且只有一个 connectionDidFinishLoading: 或 connection:didFailWithError: 消息,但不会同时收到两者。此外,一旦发送了任何一条消息,代理将不会收到给定 NSURLConnection 的更多消息。

表示您可以验证收到上述消息后不会回调委托。

于 2011-11-17T22:12:44.943 回答