作为NSURLConnection委托的对象委托的问题

时间:2011-03-07 18:12:36

标签: iphone nsurlconnection delegation

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

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

非常感谢帮助。

3 个答案:

答案 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的其他消息。

表示您可以在收到上述消息后验证是否不会回叫该代表。