SDWebImage前世今生之V2.7版本

2.7版本2012年9月8日發(fā)布

主要圍繞漸進(jìn)下載模塊支持

1. UIButton+WebCache

  • 新增BackgroundImage支持
//-------------------------2.7版本更新-block支持------------------------
//設(shè)置不同狀態(tài)的按鈕背景圖片->按鈕擴(kuò)大和縮小
- (void)setBackgroundImageWithURL:(NSURL *)url;
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;

#if NS_BLOCKS_AVAILABLE
- (void)setBackgroundImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
#endif
//---------------------------------end--------------------------------
//--------------------2.7版本更新-BackgroundImage支持-------------------
- (void)setBackgroundImageWithURL:(NSURL *)url{
    [self setBackgroundImageWithURL:url placeholderImage:nil];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder{
    [self setBackgroundImageWithURL:url placeholderImage:placeholder options:0];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options{
    SDWebImageManager *manager = [SDWebImageManager sharedManager];
    [manager cancelForDelegate:self];
    [self setBackgroundImage:placeholder forState:UIControlStateNormal];
    [self setBackgroundImage:placeholder forState:UIControlStateSelected];
    [self setBackgroundImage:placeholder forState:UIControlStateHighlighted];
    if (url){
        NSDictionary *info = [NSDictionary dictionaryWithObject:@"background" forKey:@"type"];
        [manager downloadWithURL:url delegate:self options:options userInfo:info];
    }
}
#if NS_BLOCKS_AVAILABLE
- (void)setBackgroundImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure{
    [self setBackgroundImageWithURL:url placeholderImage:nil success:success failure:failure];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;{
    [self setBackgroundImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure{
    SDWebImageManager *manager = [SDWebImageManager sharedManager];
    // Remove in progress downloader from queue
    [manager cancelForDelegate:self];
    [self setBackgroundImage:placeholder forState:UIControlStateNormal];
    [self setBackgroundImage:placeholder forState:UIControlStateSelected];
    [self setBackgroundImage:placeholder forState:UIControlStateHighlighted];
    if (url){
        NSDictionary *info = [NSDictionary dictionaryWithObject:@"background" forKey:@"type"];
        [manager downloadWithURL:url delegate:self options:options userInfo:info success:success failure:failure];
    }
}
#endif
//---------------------------------end--------------------------------

//----------------------------2.7版本更新-優(yōu)化---------------------------
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info{
    if ([[info valueForKey:@"type"] isEqualToString:@"background"]){
        [self setBackgroundImage:image forState:UIControlStateNormal];
        [self setBackgroundImage:image forState:UIControlStateSelected];
        [self setBackgroundImage:image forState:UIControlStateHighlighted];
    } else {
        [self setImage:image forState:UIControlStateNormal];
        [self setImage:image forState:UIControlStateSelected];
        [self setImage:image forState:UIControlStateHighlighted];
    }
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info{
    if ([[info valueForKey:@"type"] isEqualToString:@"background"]){
        [self setBackgroundImage:image forState:UIControlStateNormal];
        [self setBackgroundImage:image forState:UIControlStateSelected];
        [self setBackgroundImage:image forState:UIControlStateHighlighted];
    } else {
        [self setImage:image forState:UIControlStateNormal];
        [self setImage:image forState:UIControlStateSelected];
        [self setImage:image forState:UIControlStateHighlighted];
    }
}
//---------------------------------end--------------------------------

2. SDWebImageManager

  • 新增SDWebImageOptions圖片模式漸進(jìn)下載
typedef enum{
    //如果下載失敗行楞,還會(huì)繼續(xù)嘗試下載
    SDWebImageRetryFailed = 1 << 0,
    //滑動(dòng)的時(shí)候Scrollview不下載闷游,手從屏幕上移走荷并,Scrollview開始減速的時(shí)候才會(huì)開始下載圖片
    SDWebImageLowPriority = 1 << 1,
    //禁止磁盤緩存,只有內(nèi)存緩存
    SDWebImageCacheMemoryOnly = 1 << 2,
    //------------------2.7版本更新-漸進(jìn)下載----------------------
    //正常情況圖片下載完成遏插,然后一次性顯示,設(shè)置該選項(xiàng)那么圖片邊下載邊顯示
    SDWebImageProgressiveDownload = 1 << 3
    //--------------------------end----------------------------
} SDWebImageOptions;
  • 增加了block回調(diào)支持
//------------------2.7版本更新-增加block支持----------------------
#if NS_BLOCKS_AVAILABLE
typedef void(^SDWebImageSuccessBlock)(UIImage *image, BOOL cached);
typedef void(^SDWebImageFailureBlock)(NSError *error);
#endif
//--------------------------end-----------------------------
  • 新增URL緩存過(guò)濾器block
//------------------2.7版本更新-過(guò)濾URL----------------------
//[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url){
//    url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
//    return [url absoluteString];
//}];
#if NS_BLOCKS_AVAILABLE
typedef NSString *(^CacheKeyFilter)(NSURL *url);
@property (strong) CacheKeyFilter cacheKeyFilter;
#endif
//--------------------------end----------------------------
  • 廢棄了一個(gè)方法
//------------------2.7版本更新-廢棄----------------------
- (UIImage *)imageWithURL:(NSURL *)url __attribute__ ((deprecated));
//--------------------------end----------------------------
  • 新增一個(gè)下載重載方法
//------------------2.7版本更新-方法重載----------------------
//如果不在緩存中驹暑,則在給定URL處下載圖像移袍,否則返回緩存版本
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)info;
//--------------------------end-----------------------------
  • 新增下載緩存信息集合
@interface SDWebImageManager : NSObject <SDWebImageDownloaderDelegate, SDImageCacheDelegate>{
    NSMutableArray *downloaders;
    NSMutableDictionary *downloaderForURL;
    NSMutableArray *failedURLs;
    NSMutableArray *downloadDelegates;
    NSMutableArray *cacheDelegates;
    NSMutableArray *cacheURLs;
    //------------------2.7版本更新-緩存信息----------------------
    NSMutableArray *downloadInfo;
    //--------------------------end----------------------------
}

+ (id)sharedManager;

//------------------2.7版本更新-兼容block----------------------
#if NS_BLOCKS_AVAILABLE
@synthesize cacheKeyFilter;
#endif
//--------------------------end------------------------------

//-----------------------2.7版本更新-優(yōu)化-----------------------
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)userInfo{
    //非常常見(jiàn)的錯(cuò)誤是使用NSString對(duì)象而不是NSURL發(fā)送URL。出于某種奇怪的原因孝治,
    //XCode不會(huì)為這種類型不匹配拋出任何警告列粪。在這里审磁,我們通過(guò)允許url作為NSString傳遞來(lái)防止這個(gè)錯(cuò)誤。
    if ([url isKindOfClass:NSString.class]){
        url = [NSURL URLWithString:(NSString *)url];
    } else if (![url isKindOfClass:NSURL.class]){
        //防止由于NSNull等常見(jiàn)錯(cuò)誤值而導(dǎo)致的一些常見(jiàn)崩潰岂座。例如零
        url = nil;
    }
    if (!url || !delegate || (!(options & SDWebImageRetryFailed) && [failedURLs containsObject:url])){
        return;
    }
    //檢查磁盤上的緩存異步态蒂,這樣我們就不會(huì)阻塞主線程
    [cacheDelegates addObject:delegate];
    [cacheURLs addObject:url];
    NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
                          delegate,
                          @"delegate",
                          url,
                          @"url",
                          [NSNumber numberWithInt:options],
                          @"options",
                          userInfo ? userInfo : [NSNull null],
                          @"userInfo",
                          nil];
    [[SDImageCache sharedImageCache] queryDiskCacheForKey:[self cacheKeyForURL:url] delegate:self userInfo:info];
}
//-----------------------------end----------------------------

//-----------------------2.7版本更新-支持block-----------------------
- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)userInfo success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure{
    //由于iOS版本需要向后壓縮而沒(méi)有塊
    //所以從上面重復(fù)的邏輯非常常見(jiàn)的錯(cuò)誤是使用NSString對(duì)象而不是NSURL發(fā)送URL。出于某種奇怪的原因费什,
    //XCode不會(huì)為這種類型不匹配拋出任何警告钾恢。在這里,我們通過(guò)允許url作為NSString傳遞來(lái)防止這個(gè)錯(cuò)誤鸳址。
    if ([url isKindOfClass:NSString.class]){
        url = [NSURL URLWithString:(NSString *)url];
    }
    if (!url || !delegate || (!(options & SDWebImageRetryFailed) && [failedURLs containsObject:url])){
        return;
    }
    //檢查磁盤上的緩存異步瘩蚪,這樣我們就不會(huì)阻塞主線程
    [cacheDelegates addObject:delegate];
    [cacheURLs addObject:url];
    SDWebImageSuccessBlock successCopy = [success copy];
    SDWebImageFailureBlock failureCopy = [failure copy];
    NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
                          delegate, @"delegate",
                          url, @"url",
                          [NSNumber numberWithInt:options], @"options",
                          userInfo ? userInfo : [NSNull null], @"userInfo",
                          successCopy, @"success",
                          failureCopy, @"failure",
                          nil];
    SDWIRelease(successCopy);
    SDWIRelease(failureCopy);
    [[SDImageCache sharedImageCache] queryDiskCacheForKey:[self cacheKeyForURL:url] delegate:self userInfo:info];
}
#endif
//--------------------------------end------------------------------

- (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate{
    NSUInteger idx;
    while ((idx = [cacheDelegates indexOfObjectIdenticalTo:delegate]) != NSNotFound){
        [cacheDelegates removeObjectAtIndex:idx];
        [cacheURLs removeObjectAtIndex:idx];
    }
    
    while ((idx = [downloadDelegates indexOfObjectIdenticalTo:delegate]) != NSNotFound){
        SDWebImageDownloader *downloader = SDWIReturnRetained([downloaders objectAtIndex:idx]);
        //----------------------2.7版本更新-功能更新---------------------
        [downloadInfo removeObjectAtIndex:idx];
        //----------------------------end-----------------------------
        [downloadDelegates removeObjectAtIndex:idx];
        [downloaders removeObjectAtIndex:idx];
        if (![downloaders containsObject:downloader]){
            //沒(méi)有更多的委托在等待這個(gè)下載,取消它
            [downloader cancel];
            [downloaderForURL removeObjectForKey:downloader.url];
        }
        SDWIRelease(downloader);
    }
}

- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info{
    NSURL *url = [info objectForKey:@"url"];
    id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
    NSUInteger idx = [self indexOfDelegate:delegate waitingForURL:url];
    if (idx == NSNotFound){
        //請(qǐng)求已被取消
        return;
    }
    if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)]){
        [delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image];
    }
    if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)]){
        objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, url);
    }
    //--------------------------2.7版本更新-優(yōu)化--------------------------
    if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)]){
        NSDictionary *userInfo = [info objectForKey:@"userInfo"];
        if ([userInfo isKindOfClass:NSNull.class]){
            userInfo = nil;
        }
        objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, url, userInfo);
    }
    //--------------------------------end-------------------------------
#if NS_BLOCKS_AVAILABLE
    if ([info objectForKey:@"success"]){
        SDWebImageSuccessBlock success = [info objectForKey:@"success"];
        success(image, YES);
    }
#endif

    [cacheDelegates removeObjectAtIndex:idx];
    [cacheURLs removeObjectAtIndex:idx];
}

- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info{
    NSURL *url = [info objectForKey:@"url"];
    id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
    SDWebImageOptions options = [[info objectForKey:@"options"] intValue];
    NSUInteger idx = [self indexOfDelegate:delegate waitingForURL:url];
    if (idx == NSNotFound){
        //請(qǐng)求已被取消
        return;
    }

    [cacheDelegates removeObjectAtIndex:idx];
    [cacheURLs removeObjectAtIndex:idx];

    //為相同的URL共享相同的下載加載程序稿黍,所以我們不會(huì)多次下載相同的URL
    SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url];
    if (!downloader){
        downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self userInfo:info lowPriority:(options & SDWebImageLowPriority)];
        [downloaderForURL setObject:downloader forKey:url];
    } else {
        //重用共享下載器
        downloader.lowPriority = (options & SDWebImageLowPriority);
    }
    
    //------------------------2.7版本更新-優(yōu)化----------------------
    if ((options & SDWebImageProgressiveDownload) && !downloader.progressive){
        //根據(jù)需要提供漸進(jìn)式下載支持
        downloader.progressive = YES;
    }
    //----------------------------end-----------------------------

    [downloadInfo addObject:info];
    [downloadDelegates addObject:delegate];
    [downloaders addObject:downloader];
}

- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image{
    SDWIRetain(downloader);
    SDWebImageOptions options = [[downloader.userInfo objectForKey:@"options"] intValue];
    //用這個(gè)下載器通知所有下載delegate
    for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--){
        NSUInteger uidx = (NSUInteger)idx;
        SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
        if (aDownloader == downloader){
            id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
            SDWIRetain(delegate);
            SDWIAutorelease(delegate);
            if (image){
                if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)]){
                    [delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image];
                }
                if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)]){
                    objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, downloader.url);
                }
                //-------------------2.7版本更新-優(yōu)化-新增方法重載-------------------
                if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)]){
                    NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
                    if ([userInfo isKindOfClass:NSNull.class]){
                        userInfo = nil;
                    }
                    objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, downloader.url, userInfo);
                }
                //--------------------------------end---------------------------
                
                //--------------2.7版本更新-block支持優(yōu)化----------------
#if NS_BLOCKS_AVAILABLE
                if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"success"]){
                    SDWebImageSuccessBlock success = [[downloadInfo objectAtIndex:uidx] objectForKey:@"success"];
                    success(image, NO);
                }
#endif
                //-------------------------end------------------------
            } else {
                if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)]){
                    [delegate performSelector:@selector(webImageManager:didFailWithError:) withObject:self withObject:nil];
                }
                if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)]){
                    objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, nil, downloader.url);
                }
                //-------------------2.7版本更新-優(yōu)化-新增方法重載-------------------
                if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)]){
                    NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
                    if ([userInfo isKindOfClass:NSNull.class]){
                        userInfo = nil;
                    }
                    objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, nil, downloader.url, userInfo);
                }
                //--------------------------------end---------------------------
        
#if NS_BLOCKS_AVAILABLE
                if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"]){
                    SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"];
                    failure(nil);
                }
#endif
            }
            [downloaders removeObjectAtIndex:uidx];
            [downloadInfo removeObjectAtIndex:uidx];
            [downloadDelegates removeObjectAtIndex:uidx];
        }
    }
    if (image){
        //將圖像存儲(chǔ)在緩存中
        [[SDImageCache sharedImageCache] storeImage:image
                                          imageData:downloader.imageData
                                             forKey:[self cacheKeyForURL:downloader.url]
                                             toDisk:!(options & SDWebImageCacheMemoryOnly)];
    } else if (!(options & SDWebImageRetryFailed)){
        // 無(wú)法從這個(gè)URL下載圖像疹瘦,將URL標(biāo)記為failed,這樣我們就不會(huì)一次又一次地嘗試失敗
        //(只有在SDWebImageRetryFailed未被激活時(shí)才這樣做)
        [failedURLs addObject:downloader.url];
    }
    //釋放下載器
    [downloaderForURL removeObjectForKey:downloader.url];
    SDWIRelease(downloader);
}

- (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error{
    SDWIRetain(downloader);
    //用這個(gè)下載器通知所有下載delegate
    for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--){
        NSUInteger uidx = (NSUInteger)idx;
        SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
        if (aDownloader == downloader){
            id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
            SDWIRetain(delegate);
            SDWIAutorelease(delegate);
            
            if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)]){
                [delegate performSelector:@selector(webImageManager:didFailWithError:) withObject:self withObject:error];
            }
            if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)]){
                objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, error, downloader.url);
            }
            //-------------------2.7版本更新-優(yōu)化-新增方法重載-------------------
            if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)]){
                NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
                if ([userInfo isKindOfClass:NSNull.class])
                {
                    userInfo = nil;
                }
                objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, error, downloader.url, userInfo);
            }
            //--------------------------------end---------------------------
            
            //--------------2.7版本更新-block支持優(yōu)化----------------
#if NS_BLOCKS_AVAILABLE
            if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"]){
                SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"];
                failure(error);
            }
#endif
            //-------------------------end------------------------
            [downloaders removeObjectAtIndex:uidx];
            [downloadInfo removeObjectAtIndex:uidx];
            [downloadDelegates removeObjectAtIndex:uidx];
        }
    }
    [downloaderForURL removeObjectForKey:downloader.url];
    SDWIRelease(downloader);
}

//--------------------------2.7版本更新-新增功能----------------------------
- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image{
    //用這個(gè)下載器通知所有下載delegate
    for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--){
        NSUInteger uidx = (NSUInteger)idx;
        SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
        if (aDownloader == downloader){
            id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
            SDWIRetain(delegate);
            SDWIAutorelease(delegate);
            if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:)]){
                objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:), self, image, downloader.url);
            }
            if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:)]){
                NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
                if ([userInfo isKindOfClass:NSNull.class]){
                    userInfo = nil;
                }
                objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:), self, image, downloader.url, userInfo);
            }
        }
    }
}
//----------------------------------end------------------------------------

3. SDWebImageDownloader

  • 新增三個(gè)屬性
@interface SDWebImageDownloader : NSObject{
    @private
    NSURL *url;
    SDWIWeak id<SDWebImageDownloaderDelegate> delegate;
    NSURLConnection *connection;
    NSMutableData *imageData;
    id userInfo;
    BOOL lowPriority;
    
    //-----------------------2.7版本更新-功能優(yōu)化-----------------------
    NSUInteger expectedSize;
    BOOL progressive;
    size_t width, height;
    //-----------------------------end-------------------------------
}

//-----------------------2.7版本更新-功能優(yōu)化--------------
//如果設(shè)置為YES巡球,則啟用漸進(jìn)式下載支持
//正常情況圖片下載完成言沐,然后一次性顯示
//設(shè)置為YES之后邓嘹,圖片邊下載邊顯示
@property (nonatomic, readwrite) BOOL progressive;
//-----------------------------end----------------------
  • 支持漸進(jìn)下載傳遞NSData-網(wǎng)絡(luò)請(qǐng)求回調(diào)中優(yōu)化
- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response{
    //-----------------------------2.7版本更新-功能優(yōu)化-------------------------
    if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400){
        expectedSize = response.expectedContentLength > 0 ? (NSUInteger)response.expectedContentLength : 0;
        self.imageData = SDWIReturnAutoreleased([[NSMutableData alloc] initWithCapacity:expectedSize]);
        //----------------------------------end----------------------------------
    } else {
        [aConnection cancel];
        [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
        if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)]){
            NSError *error = [[NSError alloc] initWithDomain:NSURLErrorDomain
                                                        code:[((NSHTTPURLResponse *)response) statusCode]
                                                    userInfo:nil];
            [delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:error];
            SDWIRelease(error);
        }
        self.connection = nil;
        self.imageData = nil;
    }
}
  • 下載器代理漸進(jìn)下載回調(diào)
- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data{
    [imageData appendData:data];
    //-----------------------------2.7版本更新-功能優(yōu)化-------------------------
    if (&CGImageSourceCreateImageAtIndex == NULL){
        //ImageIO在iOS < 4中不存在
        self.progressive = NO;
    }
    if (self.progressive && expectedSize > 0 && [delegate respondsToSelector:@selector(imageDownloader:didUpdatePartialImage:)]){
        //以下代碼來(lái)自http://www.cocoaintheshell.com/2011/05/progressies-download-imageio/
        //感謝作者@Nyx0uf
        //獲取下載的總字節(jié)數(shù)
        const NSUInteger totalSize = [imageData length];
        //更新數(shù)據(jù)源時(shí),我們必須傳遞所有數(shù)據(jù)险胰,而不僅僅是新字節(jié)
        CGImageSourceRef imageSource = CGImageSourceCreateIncremental(NULL);
#if __has_feature(objc_arc)
        CGImageSourceUpdateData(imageSource, (__bridge  CFDataRef)imageData, totalSize == expectedSize);
#else
        CGImageSourceUpdateData(imageSource, (CFDataRef)imageData, totalSize == expectedSize);
#endif
        if (width + height == 0){
            CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
            if (properties){
                CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
                if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
                val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
                if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
                CFRelease(properties);
            }
        }
        if (width + height > 0 && totalSize < expectedSize){
            //創(chuàng)建圖像
            CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
#ifdef TARGET_OS_IPHONE
            //iOS變形圖像的變通方法
            if (partialImageRef){
                const size_t partialHeight = CGImageGetHeight(partialImageRef);
                CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
                CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
                CGColorSpaceRelease(colorSpace);
                if (bmContext){
                    CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
                    CGImageRelease(partialImageRef);
                    partialImageRef = CGBitmapContextCreateImage(bmContext);
                    CGContextRelease(bmContext);
                } else {
                    CGImageRelease(partialImageRef);
                    partialImageRef = nil;
                }
            }
#endif
            if (partialImageRef){
                UIImage *image = SDScaledImageForPath(url.absoluteString, [UIImage imageWithCGImage:partialImageRef]);
                [[SDWebImageDecoder sharedImageDecoder] decodeImage:image
                                                       withDelegate:self
                                                           userInfo:[NSDictionary dictionaryWithObject:@"partial" forKey:@"type"]];
                CGImageRelease(partialImageRef);
            }
        }
        CFRelease(imageSource);
    }
    //----------------------------------end----------------------------------
}

- (void)imageDecoder:(SDWebImageDecoder *)decoder didFinishDecodingImage:(UIImage *)image userInfo:(NSDictionary *)aUserInfo{
    //-----------------------------2.7版本更新-功能優(yōu)化-------------------------
    if ([[aUserInfo valueForKey:@"type"] isEqualToString:@"partial"]){
        [delegate imageDownloader:self didUpdatePartialImage:image];
    } else {
        [delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image];
    }
    //----------------------------------end----------------------------------
}

4. SDImageCache

  • 增加緩存監(jiān)控
//---------------2.7版本更新-新增功能--------------------
//設(shè)置全局最大緩存年齡
+ (void) setMaxCacheAge:(NSInteger) maxCacheAge;
//獲取磁盤緩存中的映像數(shù)
- (int)getDiskCount;
//獲取內(nèi)存緩存中圖像的總大小
- (int)getMemorySize;
//獲取內(nèi)存緩存中的圖像數(shù)量
- (NSUInteger)getMemoryCount;
//---------------------------end----------------------
  • 緩存釋放優(yōu)化
//---------------2.7版本更新-新增功能-內(nèi)存--------------------
static natural_t minFreeMemLeft = 1024*1024*12; // reserve 12MB RAM
// inspired by http://stackoverflow.com/questions/5012886/knowing-available-ram-on-an-ios-device
//確定iOS設(shè)備上的可用RAM量
static natural_t get_free_memory(void){
    mach_port_t host_port;
    mach_msg_type_number_t host_size;
    vm_size_t pagesize;
    host_port = mach_host_self();
    host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    host_page_size(host_port, &pagesize);
    vm_statistics_data_t vm_stat;
    if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS){
        NSLog(@"Failed to fetch vm statistics");
        return 0;
    }

    //統(tǒng)計(jì)數(shù)據(jù)以字節(jié)為單位
    natural_t mem_free = vm_stat.free_count * pagesize;
    return mem_free;
}
//---------------------------end----------------------

*********
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk{
    if (!image || !key){
        return;
    }
    //-----------------------2.7版本更新-功能優(yōu)化-----------------------
    if (get_free_memory() < minFreeMemLeft){
        [memCache removeAllObjects];
    }
    //------------------------------end------------------------------
    [memCache setObject:image forKey:key];

    if (toDisk){
        NSArray *keyWithData;
        if (data){
            keyWithData = [NSArray arrayWithObjects:key, data, nil];
        } else{
            keyWithData = [NSArray arrayWithObjects:key, nil];
        }
        NSInvocationOperation *operation = SDWIReturnAutoreleased([[NSInvocationOperation alloc] initWithTarget:self
                                                                                                       selector:@selector(storeKeyWithDataToDisk:)
                                                                                                         object:keyWithData]);
        [cacheInQueue addOperation:operation];
    }
}

- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk{
    if (key == nil){
        return nil;
    }
    UIImage *image = [memCache objectForKey:key];
    if (!image && fromDisk){
        image = SDScaledImageForPath(key, [NSData dataWithContentsOfFile:[self cachePathForKey:key]]);
        if (image){
            //-----------------------2.7版本更新-功能優(yōu)化-----------------------
            if (get_free_memory() < minFreeMemLeft){
                [memCache removeAllObjects];
            }
            //------------------------------end------------------------------
            [memCache setObject:image forKey:key];
        }
    }
    return image;
}

- (void)notifyDelegate:(NSDictionary *)arguments{
    NSString *key = [arguments objectForKey:@"key"];
    id <SDImageCacheDelegate> delegate = [arguments objectForKey:@"delegate"];
    NSDictionary *info = [arguments objectForKey:@"userInfo"];
    UIImage *image = [arguments objectForKey:@"image"];
    
    if (image){
        //-----------------------2.7版本更新-功能優(yōu)化-----------------------
        if (get_free_memory() < minFreeMemLeft){
            [memCache removeAllObjects];
        }
        //------------------------------end------------------------------
        [memCache setObject:image forKey:key];
        if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]){
            [delegate imageCache:self didFindImage:image forKey:key userInfo:info];
        }
    } else {
        if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]){
            [delegate imageCache:self didNotFindImageForKey:key userInfo:info];
        }
    }
}

//----------------------2.7版本更新-新增功能------------------------
+ (void) setMaxCacheAge:(NSInteger)maxCacheAge{
    cacheMaxCacheAge = maxCacheAge;
}
- (int)getDiskCount{
    int count = 0;
    NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath];
    for (NSString *fileName in fileEnumerator){
        count += 1;
    }
    return count;
}
- (int)getMemorySize{
    int size = 0;
    for(id key in [memCache allKeys]){
        UIImage *img = [memCache valueForKey:key];
        size += [UIImageJPEGRepresentation(img, 0) length];
    }
    return size;
}
- (NSUInteger)getMemoryCount{
    return [[memCache allKeys] count];
}
//----------------------------end--------------------------------

5. SDWebImagePrefetcher

  • 增加下載模式支持
//--------------------2.7版本更新-圖片下載---------------------
@property (nonatomic, assign) SDWebImageOptions options;
//----------------------------end---------------------------
//---------------2.7版本更新-新增功能----------------
@synthesize options;
//----------------------end-----------------------

+ (SDWebImagePrefetcher *)sharedImagePrefetcher{
    if (instance == nil){
        instance = [[SDWebImagePrefetcher alloc] init];
        instance.maxConcurrentDownloads = 3;
        //---------------2.7版本更新-新增功能----------------
        instance.options = (SDWebImageLowPriority);
        //----------------------end-----------------------
    }
    return instance;
}

6. SDWebImageCompat

  • 針對(duì)圖片數(shù)據(jù)類型兼容
NS_INLINE UIImage *SDScaledImageForPath(NSString *path, NSObject *imageOrData){
    if (!imageOrData){
        return nil;
    }

    //---------------------2.7版本更新-代碼優(yōu)化---------------------
    UIImage *image = nil;
    if ([imageOrData isKindOfClass:[NSData class]]){
        image = [[UIImage alloc] initWithData:(NSData *)imageOrData];
    } else if ([imageOrData isKindOfClass:[UIImage class]]){
        image = SDWIReturnRetained((UIImage *)imageOrData);
    } else {
        return nil;
    }
    //----------------------------end----------------------------
    
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]){
        CGFloat scale = 1.0;
        if (path.length >= 8){
            NSRange range = [path rangeOfString:@"@2x." options:0 range:NSMakeRange(path.length - 8, 5)];
            if (range.location != NSNotFound){
                scale = 2.0;
            }
        }
        UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:UIImageOrientationUp];
        SDWISafeRelease(image)
        image = scaledImage;
    }
    return SDWIReturnAutoreleased(image);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汹押,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子起便,更是在濱河造成了極大的恐慌棚贾,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榆综,死亡現(xiàn)場(chǎng)離奇詭異鸟悴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)奖年,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門细诸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人陋守,你說(shuō)我怎么就攤上這事震贵。” “怎么了水评?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵猩系,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我中燥,道長(zhǎng)寇甸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任疗涉,我火速辦了婚禮拿霉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咱扣。我一直安慰自己绽淘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布闹伪。 她就那樣靜靜地躺著沪铭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪偏瓤。 梳的紋絲不亂的頭發(fā)上杀怠,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音厅克,去河邊找鬼赔退。 笑死,一個(gè)胖子當(dāng)著我的面吹牛已骇,可吹牛的內(nèi)容都是我干的离钝。 我是一名探鬼主播票编,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼卵渴!你這毒婦竟也來(lái)了慧域?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浪读,失蹤者是張志新(化名)和其女友劉穎昔榴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碘橘,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡互订,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了痘拆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仰禽。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纺蛆,靈堂內(nèi)的尸體忽然破棺而出吐葵,到底是詐尸還是另有隱情,我是刑警寧澤桥氏,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布温峭,位于F島的核電站,受9級(jí)特大地震影響字支,放射性物質(zhì)發(fā)生泄漏凤藏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一堕伪、第九天 我趴在偏房一處隱蔽的房頂上張望揖庄。 院中可真熱鬧,春花似錦刃跛、人聲如沸抠艾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至腌歉,卻和暖如春蛙酪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翘盖。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工桂塞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人馍驯。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓阁危,卻偏偏與公主長(zhǎng)得像玛痊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狂打,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容