We have used SDWebImage Lib on Our App.(SDWebImage Version 3.7.2) Recently, the app terminate abnormally sometimes.(In SDWebImageDownloaderOperation#cancelInternal.)
the following is stacktrace.
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:
Abort point is following.
[imageButton sd_setImageWithURL:[NSURL URLWithString:url] forState:UIControlStateNormal placeholderImage:nil options:SDWebImageRetryFailed];
(It's called Collection view cell 's layoutSubviews)
CollectionView Cell's View Source code. 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
@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
MyCollectionViewController
- (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;
}
Why terminate abnormally?
How to avoid this error should I do?
(Is it Bad Access Memory?)
Edit
thanks. It has not been released yet, and modify the code as follows
PhotoCollectionViewCell(fixed.)
@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
MyCollectionViewController(fixed)
- (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;
}
When you use SDWebImageView or SDWebImageButton in UITableViewCell and UICollectionViewCell, you have to care about 'cell's reuse timing' and 'asynchronous download and set image timing'.
This happens when you scroll fast, so when the asynchronous download completed, the ui is already reused by another cell.
To avoid this error (I guess),
use SDWebImage api in
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
or
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
and grab the UI's pointer with '__block'
If you show more code about 'cellForItemAtIndexPath', I can help you.