ASIHTTPRequest dealloc和EXC_BAD_ACCESS问题

时间:2010-07-20 15:48:18

标签: iphone objective-c exc-bad-access asihttprequest dealloc

我正在使用ASIHTTPRequest包装器数组(AsyncImageLoader)来下载UITableView中单元格的图像。

我在处理ASIHTTPRequests生命周期时遇到问题。如果我发布它们,如果我在尝试加载图像时继续向上和向下滚动,我最终会有一个EXC_BAD_ACCESS。

这是我的包装器的样子。 self.request具有保留属性,targetCell是我要将图像放入的单元格:

@implementation AsyncImageLoader
- (void)loadImageFromURL:(NSString*)fileName target:(ResultTableViewCell*)targetCell {
    // check for cached images, etc

    NSURL* url = [NSURL URLWithString:fileName];
    [self.request cancel];
    self.request = [ASIHTTPRequest requestWithURL:url];
    self.request.delegate = self;
}

- (void)startLoading {
    [self.request startAsynchronous];
}

- (void)cancel {
    [self.request cancel];
    self.request = nil;
}

- (void)requestFinished:(ASIHTTPRequest*)requestDone {
    // cache image, set cell image...

    self.request = nil;
}

- (void)requestFailed:(ASIHTTPRequest*)requestDone {
    // handle answer as well

    self.request = nil;
}
@end
loadImageFromURL中为cellForRowAtIndexPath AsyncImageLoader调用

indexPath.row % 6,因此如果我继续向上和向下滚动,则会使用相同的对象一次又一次地调用它,取消请求,因为它们不是还没结束。

但我总是有一个EXC_BAD_ACCESS。根据调用堆栈,它发生在由markAsFinished调用的failWithError中,由[self.request cancel]中的loadImageFromURL:调用。大部分时间ASIHTTPRequest已经发布了(我在它的dealloc中添加了一个NSLog),但是我没有看到这是如何可能的,因为ASIHTTPRequest似乎抛出了保留因此在取消时它没有被释放。

如果我在委托方法中删除self.request = nil,我没有任何EXC_BAD_ACCESS,但由于ASIHTTPRequests在没有重新分配的情况下继续创建,它们最终都无法正常工作。

有人能告诉我我做错了什么吗?

4 个答案:

答案 0 :(得分:5)

当您遇到EXC_BAD_ACCESS的问题时,这意味着您已经过度发布了某些内容。在大多数情况下,您只需运行调试器(Command-Y)并在崩溃时查看堆栈跟踪中您的代码的最后一行,以找出哪个对象被过度释放。如果这不起作用,那么您需要turn on zombies并在发布后再次查看正在访问的对象。这几乎总能指向正确的方向。

作为旁注,有一些其他的库可以做你想做的事情。我没有多用过这个,但看起来它至少有潜力。退房:SDWebImage。也许这会给你一些想法。

最好的问候。

答案 1 :(得分:2)

这听起来很像我看到的崩溃。

我还不确定原因是什么,但我坚信它与客户端代码无关(即它在ASIHTTPRequest或操作系统中)。

我已经解决了asi http请求中的一些问题,但这种崩溃仍然存在。

我一直在努力寻求苹果论坛的帮助:

https://devforums.apple.com/thread/59843?tstart=0

以下是崩溃对我来说的样子:

#2  0x32c02e14 in CFRetain ()
#3  0x32c709b6 in __CFTypeCollectionRetain ()
#4  0x32c06c60 in _CFArrayReplaceValues ()
#5  0x32b0994c in -[NSCFArray insertObject:atIndex:] ()
#6  0x32b098f0 in -[NSCFArray addObject:] ()
#7  0x32b709f0 in __chooseAll ()
#8  0x32b71bde in __finishedOp ()
#9  0x32b26636 in +[NSOperation observeValueForKeyPath:ofObject:change:context:] ()
#10 0x32b265aa in NSKVONotify ()
#11 0x32b13306 in -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] ()
#12 0x0007d6ec in -[ASIHTTPRequest markAsFinished] (self=0x5a7cc40, _cmd=0xa1710) at ASIHTTPRequest.m:2817
#13 0x00077e02 in -[ASIHTTPRequest failWithError:] (self=0x5a7cc40, _cmd=0x330cdfc4, theError=0x248130) at ASIHTTPRequest.m:1708
#14 0x000712dc in -[ASIHTTPRequest cancel] (self=0x5a7cc40, _cmd=0x322b4298) at ASIHTTPRequest.m:515
#15 0x0008884c in -[UIHTTPImageView setImageWithURL:placeholderImage:] (self=0x5a7c9d0, _cmd=0xa3f0c, url=0x5aa7760, placeholder=0x0) at UIHTTPImageView.m:21
#16 0x0006183c in -[MeetingView tiledScrollView:tileForRow:column:resolution:] (self=0x5a32560, _cmd=0x9bfac, tiledScrollView=0x5a74570, row=1, column=0, resolution=0) at MeetingView.m:1053
#17 0x0000475e in -[TiledScrollView layoutSubviews] (self=0x5a74570, _cmd=0x32299680) at TiledScrollView.m:181
#18 0x31515f32 in -[UIView(CALayerDelegate) _layoutSublayersOfLayer:] ()
#19 0x32c29ffa in -[NSObject performSelector:withObject:] ()
#20 0x30964798 in -[CALayer layoutSublayers] ()
#21 0x30964568 in CALayerLayoutIfNeeded ()
#22 0x30959b62 in CA::Context::commit_transaction ()
#23 0x3095997a in CA::Transaction::commit ()
#24 0x3097f164 in CA::Transaction::observer_callback ()
#25 0x32c702a0 in __CFRunLoopDoObservers ()
#26 0x32c23bb0 in CFRunLoopRunSpecific ()
#27 0x32c234e0 in CFRunLoopRunInMode ()
#28 0x30d620da in GSEventRunModal ()
#29 0x30d62186 in GSEventRun ()
#30 0x314d54c8 in -[UIApplication _run] ()
#31 0x314d39f2 in UIApplicationMain ()
#32 0x0000245c in main (argc=1, argv=0x2ffff5b4) at main.m:14

据我所知,这些行为看起来像这样:

  1. 创建并启动了请求,因此将其添加到队列
  2. 请求在开始运行之前被取消
  3. 请求已成功取消,已从队列中删除并已取消分配
  4. 另一个请求完成,并且队列比调用保留在释放的请求
  5. 作为一项实验,我尝试了一次额外的保留请求 - 这会停止崩溃,但队列会停止运行。

    ASIHTTPRequest可能仍然以某种方式错误处理取消请求,但我发现很难看出如何。

    <强>更新

    这应该解决它:

    http://github.com/jogu/asi-http-request/commit/887fcad0f77e9717f003273612804a9b9012a140

    据我所知,NSOperationQueue在被告知未完成的请求已经完成后进入了糟糕状态。

答案 2 :(得分:1)

回答自己作为我的结论和其他答案的综合。

似乎ASIHTTPRequest(或者iOS)在取消请求时有点不稳定,特别是当有多个请求时,很多请求被取消并且很快被释放。

作为一种解决方案,我放弃了创建许多请求的设计,如果不再需要它们就取消它们。我创建了一个在ASIHTTPRequest中存储NSOperationQueue的包装器,它也会过滤网址,因此相同的请求不会启动两次(如果单元格出现,则在图像完全加载之前消失,并显示再次)。我将所有请求结果(即图像)存储在缓存中。

这会导致网络活动可能略高一些,但是每个新请求都完成后(即使图像可能无法显示)并缓存,其他所有内容都更清晰,因此不需要创建/取消等等...因此不再崩溃。

答案 3 :(得分:0)

尝试在视图的dealloc中添加这样的内容:

    [self.thumbnailRequest cancel];
self.thumbnailRequest.delegate = nil;
[self.thumbnailRequest setDidFinishSelector:NULL];
[self.thumbnailRequest setDidFailSelector:NULL];
self.thumbnailRequest = nil;