有时应用程序在 SDWebImage 中崩溃(SDWebImageDownloaderOperation#cancelInternal)

Sometimes app crash in SDWebImage(SDWebImageDownloaderOperation#cancelInternal)

我们在我们的应用程序上使用了 SDWebImage Lib。(SDWebImage 版本 3.7.2) 最近,应用程序有时会异常终止。(在 SDWebImageDownloaderOperation#cancelInternal.)

以下是堆栈跟踪。

Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0xxx

Thread : Crashed: com.apple.main-thread
0 libobjc.A.dylib 0x180765bd0 objc_msgSend + 16
1 HOGE 0x1002a94d8 -SDWebImageDownloaderOperation cancelInternal
2 HOGE 0x1002a93ec -SDWebImageDownloaderOperation cancel
3 HOGE 0x1002ad63c __69-[SDWebImageManager downloadImageWithURL:options:progress:completed:]_block_invoke131 (SDWebImageManager.m:245)
4 HOGE 0x1002ae0f0 -SDWebImageCombinedOperation cancel
5 HOGE 0x1002b5380 -UIView(WebCacheOperation) sd_cancelImageLoadOperationWithKey:
6 HOGE 0x1002b0c74 -UIButton(WebCache) sd_cancelImageLoadForState:
7 HOGE 0x1002af578 -UIButton(WebCache) sd_setImageWithURL:forState:placeholderImage:options:completed:
8 HOGE 0x1002af3f8 -UIButton(WebCache) sd_setImageWithURL:forState:placeholderImage:options:

中止点如下。

[imageButton sd_setImageWithURL:[NSURL URLWithString:url] forState:UIControlStateNormal placeholderImage:nil options:SDWebImageRetryFailed]; 

(它叫做 Collection view cell 的 layoutSubviews)

Collection查看单元格的查看源代码。 Header

#import <UIKit/UIKit.h>
#import "Photo.h"

@interface PhotoCollectionViewCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UIButton *imageButton;
@property (strong, nonatomic) UIButton *likeButton;
@property (strong, nonatomic) Photo *photo;

@end

实施

@implementation PhotoCollectionViewCell

- (void)awakeFromNib
{
    self.selectedBackgroundView = [[UIView alloc] initWithFrame:self.bounds];
    self.selectedBackgroundView.backgroundColor = [BaseColorSet getColor1];

    [_imageButton setBackgroundColor:[BaseColorSet getPhotoBackgroundColor]];
    _imageButton.layer.cornerRadius = 2;
    _imageButton.clipsToBounds = YES;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    NSString *url = [PhotoImgURL get_5:_photo];
    if (!url) {
        url = [NSString stringWithFormat:@"%@/%@/%@",S3_STORAGE_URL,[PhotoDirectory get_5],_photo.filename];
    }

    [_imageButton sd_setImageWithURL:[NSURL URLWithString:url] forState:UIControlStateNormal placeholderImage:nil options:SDWebImageRetryFailed];
    _imageButton.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
    _imageButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
    _imageButton.imageView.contentMode = UIViewContentModeScaleToFill;
}

@end

我的CollectionViewController

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    PhotoCollectionViewCell *cell = (PhotoCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
    cell.photo = [_photos objectAtIndex:indexPath.row];
    [cell.imageButton addTarget:self action:@selector(imageButtonPushed:) forControlEvents:UIControlEventTouchUpInside];
    [cell setNeedsLayout];

    // add Load processing
    if (indexPath.row == _photos.count-1 && !_isLast) {
        Photo *photo = [_photos objectAtIndex:indexPath.row];
        [self pagingMyView:photo];
    }

    return cell;
}

为什么异常终止?

我应该如何避免这个错误?

(是坏存取内存吗?)

编辑

谢谢。 暂未发布,修改代码如下

照片CollectionViewCell(固定。)

@implementation PhotoCollectionViewCell

- (void)awakeFromNib
{
    self.selectedBackgroundView = [[UIView alloc] initWithFrame:self.bounds];
    self.selectedBackgroundView.backgroundColor = [BaseColorSet getColor1];

    [_imageButton setBackgroundColor:[BaseColorSet getPhotoBackgroundColor]];
    _imageButton.layer.cornerRadius = 2;
    _imageButton.clipsToBounds = YES;
    _imageButton.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
    _imageButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
    _imageButton.imageView.contentMode = UIViewContentModeScaleToFill;
}

-(void)prepareForReuse
{
    [super prepareForReuse];
    [_imageButton sd_cancelImageLoadForState:UIControlStateNormal];
}

@end

我的CollectionViewController(固定)

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    PhotoCollectionViewCell *cell = (PhotoCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
    cell.photo = [_photos objectAtIndex:indexPath.row];
    cell.imageButton.tag = cell.photo.photoId;
    NSString *url = [PhotoImgURL get_5:cell.photo];
    if (!url) {
        url = [NSString stringWithFormat:@"%@/%@/%@",S3_STORAGE_URL,[PhotoDirectory get_5],cell.photo.filename];
    }
    [cell.imageButton sd_setImageWithURL:[NSURL URLWithString:url] forState:UIControlStateNormal placeholderImage:nil options:SDWebImageRetryFailed];
    [cell.imageButton addTarget:self action:@selector(imageButtonPushed:) forControlEvents:UIControlEventTouchUpInside];
    [cell setNeedsLayout];

    // add Load processing
    if (indexPath.row == _photos.count-1 && !_isLast) {
        Photo *photo = [_photos objectAtIndex:indexPath.row];
        [self pagingMyView:photo];
    }

    return cell;
}

在UITableViewCell 和UICollectionViewCell 中使用SDWebImageView 或SDWebImageButton 时,您必须注意'cell's reuse timing' 和'asynchronous download and set image timing'。

快速滚动时会发生这种情况,因此当异步下载完成时,ui 已被另一个单元格重用。

为了避免这个错误(我猜),

中使用 SDWebImage api
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

并用'__block'

获取UI的指针

如果您提供更多关于 'cellForItemAtIndexPath' 的代码,我可以帮助您。