2

我正在使用一组 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

loadImageFromURLcellForRowAtIndexPathindexPath.row % 6一个 AsyncImageLoader 调用,所以如果我继续向上和向下滚动,它会用同一个对象一次又一次地调用,取消请求,因为它们还没有结束。

但我最终总是有一个 EXC_BAD_ACCESS。根据调用堆栈,它发生在 ASIHTTPRequest 中markAsFinished,由 in 调用failWithError,由[self.request cancel]in调用loadImageFromURL:。大多数时候 ASIHTTPRequest 已经发布(我在它的 dealloc 中添加了一个 NSLog),但我不明白这是怎么可能的,因为 ASIHTTPRequest 似乎抛出了保留,因此在取消时它不会被释放。

如果我在委托方法中删除,我没有任何 EXC_BAD_ACCESS self.request = nil,但是由于 ASIHTTPRequests 在没有释放的情况下继续创建,它们最终根本不起作用。

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

4

4 回答 4

5

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

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

最好的祝福。

于 2010-07-20T17:06:27.663 回答
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. 另一个请求完成,并且队列比调用保留在解除分配的请求上

作为一项实验,我尝试了一次保留请求 - 这会停止崩溃,但队列会停止运行。

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

更新

这应该解决它:

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

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

于 2010-07-21T18:40:31.320 回答
1

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

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

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

这可能会导致网络活动略高,但随着每个新请求的完成(即使图像可能未显示)和缓存,其他一切都变得更加清晰,因此无需处理创建/取消/等...因此没有更多崩溃。

于 2010-07-21T19:34:25.197 回答
0

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

    [self.thumbnailRequest cancel];
self.thumbnailRequest.delegate = nil;
[self.thumbnailRequest setDidFinishSelector:NULL];
[self.thumbnailRequest setDidFailSelector:NULL];
self.thumbnailRequest = nil;
于 2012-10-15T12:40:21.557 回答