我正在使用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在没有重新分配的情况下继续创建,它们最终都无法正常工作。
有人能告诉我我做错了什么吗?
答案 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
据我所知,这些行为看起来像这样:
作为一项实验,我尝试了一次额外的保留请求 - 这会停止崩溃,但队列会停止运行。
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;