转载

学习 SDWebImage

Basic Information

  • Name : SDWebImage
  • Site : https://github.com/rs/SDWebImage
  • Repo : https://github.com/rs/SDWebImage
  • Revision : 0da78a4ce6485d8d4c23d348d355fabad7c227f3
  • Description : 经典的、异步下载图片的、带缓存的库

Global Note

File Notes

0. SDWebImageCompat.h

  • Path : /SDWebImage/SDWebImageCompat.h
  • Line : 60 - 72
  • Note :
#define dispatch_main_sync_safe(block)/     if ([NSThread isMainThread]) {/         block();/     } else {/         dispatch_sync(dispatch_get_main_queue(), block);/     }  #define dispatch_main_async_safe(block)/     if ([NSThread isMainThread]) {/         block();/     } else {/         dispatch_async(dispatch_get_main_queue(), block);/     }

方便的dispatch_main 保证主线程运行

1. SDWebImageDownloader.m

  • Path : /SDWebImage/SDWebImageDownloader.m
  • Line : 33 - 33
  • Note :
    if (NSClassFromString(@"SDNetworkActivityIndicator")) {

判断某个类是否存在

2. SDWebImageDownloader.m

  • Path : /SDWebImage/SDWebImageDownloader.m
  • Line : 35 - 38
  • Note :
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks"         id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")]; #pragma clang diagnostic pop

动态调用单例方法

3. SDWebImageDownloader.m

  • Path : /SDWebImage/SDWebImageDownloader.m
  • Line : 67 - 68
  • Note :
        _downloadQueue = [NSOperationQueue new];         _downloadQueue.maxConcurrentOperationCount = 6;

NSOperation相比GCD的优势,可配置并行最大线程数

4. SDWebImageDownloader.m

  • Path : /SDWebImage/SDWebImageDownloader.m
  • Line : 111 - 113
  • Note :
- (void)setOperationClass:(Class)operationClass {     _operationClass = operationClass ?: [SDWebImageDownloaderOperation class]; }

这么动态配置执行的Class。

5. SDWebImageDownloader.m

  • Path : /SDWebImage/SDWebImageDownloader.m
  • Line : 207 - 207
  • Note :
    dispatch_barrier_sync(self.barrierQueue, ^{
    _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); 

在并行queue中的很方便的同步方法。

6. SDWebImageDownloader.m

  • Path : /SDWebImage/SDWebImageDownloader.m
  • Line : 214 - 224
  • Note :
        // Handle single download of simultaneous download request for the same URL         NSMutableArray *callbacksForURL = self.URLCallbacks[url];         NSMutableDictionary *callbacks = [NSMutableDictionary new];         if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];         if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];         [callbacksForURL addObject:callbacks];         self.URLCallbacks[url] = callbacksForURL;          if (first) {             createCallback();         }

每个URL对应多个回调地址。可能同时多次请求相同URL地址,避免重复下载。

7. SDWebImageDownloader.m

  • Path : /SDWebImage/SDWebImageDownloader.m
  • Line : 187 - 192
  • Note :
        [wself.downloadQueue addOperation:operation];         if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {             // Emulate LIFO execution order by systematically adding new operations as last operation's dependency             [wself.lastAddedOperation addDependency:operation];             wself.lastAddedOperation = operation;         }

启动下载。如果是Last In First Out,则把最后一个添加的任务的依赖设置为当前要添加的任务。

8. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 33 - 33
  • Note :
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;

后台任务ID

9. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 77 - 92
  • Note :
        Class UIApplicationClass = NSClassFromString(@"UIApplication");         BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];         if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {             __weak __typeof__ (self) wself = self;             UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];             self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{                 __strong __typeof (wself) sself = wself;                  if (sself) {                     [sself cancel];                      [app endBackgroundTask:sself.backgroundTaskId];                     sself.backgroundTaskId = UIBackgroundTaskInvalid;                 }             }];         }

后台,保证任务取消

10. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 96 - 96
  • Note :
        self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];

发起下载操作的NSURLConnection

11. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 100 - 123
  • Note :
    [self.connection start];      if (self.connection) {         if (self.progressBlock) {             self.progressBlock(0, NSURLResponseUnknownLength);         }         dispatch_async(dispatch_get_main_queue(), ^{             [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];         });          if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) {             // Make sure to run the runloop in our background thread so it can process downloaded data             // Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5             //       not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466)             CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);         }         else {             CFRunLoopRun();         }          if (!self.isFinished) {             [self.connection cancel];             [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]];         }

发起下载。NSRunLoopRun() 启动后台线程的runloop。NSURLConnection 依赖RunLoop。

SDWebImage是个古老的库,兼容iOS5.1以下。

12. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 155 - 167
  • Note :
- (void)cancelInternalAndStop {     if (self.isFinished) return;     [self cancelInternal];     CFRunLoopStop(CFRunLoopGetCurrent()); }  - (void)cancelInternal {     if (self.isFinished) return;     [super cancel];     if (self.cancelBlock) self.cancelBlock();      if (self.connection) {         [self.connection cancel];

先cancel Connection,然后停止RunLoop。

13. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 196 - 200
  • Note :
- (void)setFinished:(BOOL)finished {     [self willChangeValueForKey:@"isFinished"];     _finished = finished;     [self didChangeValueForKey:@"isFinished"]; }

KVO

14. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 213 - 225
  • Note :
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {          //'304 Not Modified' is an exceptional one     if (![response respondsToSelector:@selector(statusCode)] || ([((NSHTTPURLResponse *)response) statusCode] < 400 && [((NSHTTPURLResponse *)response) statusCode] != 304)) {         NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0;         self.expectedSize = expected;         if (self.progressBlock) {             self.progressBlock(0, expected);         }          self.imageData = [[NSMutableData alloc] initWithCapacity:expected];         self.response = response;

看来有个 304 Not Modified 坑。

15. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 231 - 239
  • Note :
        NSUInteger code = [((NSHTTPURLResponse *)response) statusCode];                  //This is the case when server returns '304 Not Modified'. It means that remote image is not changed.         //In case of 304 we need just cancel the operation and return cached image from the cache.         if (code == 304) {             [self cancelInternal];         } else {             [self.connection cancel];         }

304 ,则直接返回缓存中的图片

16. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 252 - 253
  • Note :
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {     [self.imageData appendData:data];

收到数据。self.imageData 是从didReceiveResponse中开辟。

17. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 256 - 266
  • Note :
        // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/         // Thanks to the author @Nyx0uf          // Get the total bytes downloaded         const NSInteger totalSize = self.imageData.length;          // Update the data source, we must pass ALL the data, not just the new bytes         CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL);          if (width + height == 0) {             CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);

渐进的加载图片……内存中绘制渐进效果的图片

18. SDWebImageDownloaderOperation.m

  • Path : /SDWebImage/SDWebImageDownloaderOperation.m
  • Line : 337 - 358
  • Note :
+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value {     switch (value) {         case 1:             return UIImageOrientationUp;         case 3:             return UIImageOrientationDown;         case 8:             return UIImageOrientationLeft;         case 6:             return UIImageOrientationRight;         case 2:             return UIImageOrientationUpMirrored;         case 4:             return UIImageOrientationDownMirrored;         case 5:             return UIImageOrientationLeftMirrored;         case 7:             return UIImageOrientationRightMirrored;         default:             return UIImageOrientationUp;     } }

关于Orientation ,参考这篇文章 http://feihu.me/blog/2015/how-to-handle-image-orientation-on-iOS/

19. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 14 - 35
  • Note :
// See https://github.com/rs/SDWebImage/pull/1141 for discussion @interface AutoPurgeCache : NSCache @end  @implementation AutoPurgeCache  - (id)init {     self = [super init];     if (self) {         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];     }     return self; }  - (void)dealloc {     [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];  }  @end

内存警告时清空缓存

20. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 38 - 53
  • Note :
// PNG signature bytes and data (below) static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; static NSData *kPNGSignatureData = nil;  BOOL ImageDataHasPNGPreffix(NSData *data);  BOOL ImageDataHasPNGPreffix(NSData *data) {     NSUInteger pngSignatureLength = [kPNGSignatureData length];     if ([data length] >= pngSignatureLength) {         if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) {             return YES;         }     }      return NO; }

png文件前缀

21. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 55 - 57
  • Note :
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {     return image.size.height * image.size.width * image.scale * image.scale; }

FOUNDATION_STATIC_INLINE 可以学习用用。 图片的cost计算方式。

22. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 77 - 77
  • Note :
        instance = [self new];

[self new] 学习了。可以研究下 与[XXXX new]的区别。

23. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 105 - 105
  • Note :
        _memCache = [[AutoPurgeCache alloc] init];

内存的缓存是NSCache。

24. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 125 - 127
  • Note :
        dispatch_sync(_ioQueue, ^{             _fileManager = [NSFileManager new];         });

ioQueue

_ioQueue = dispatch_queue_create(“com.hackemist.SDWebImageCache”, DISPATCH_QUEUE_SERIAL);

25. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 177 - 189
  • Note :
- (NSString *)cachedFileNameForKey:(NSString *)key {     const char *str = [key UTF8String];     if (str == NULL) {         str = "";     }     unsigned char r[CC_MD5_DIGEST_LENGTH];     CC_MD5(str, (CC_LONG)strlen(str), r);     NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",                           r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],                           r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];      return filename; }

磁盘缓存文件名称

26. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 213 - 238
  • Note :
            if (image && (recalculate || !data)) { #if TARGET_OS_IPHONE                 // We need to determine if the image is a PNG or a JPEG                 // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html)                 // The first eight bytes of a PNG file always contain the following (decimal) values:                 // 137 80 78 71 13 10 26 10                  // If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download)                 // and the image has an alpha channel, we will consider it PNG to avoid losing the transparency                 int alphaInfo = CGImageGetAlphaInfo(image.CGImage);                 BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||                                   alphaInfo == kCGImageAlphaNoneSkipFirst ||                                   alphaInfo == kCGImageAlphaNoneSkipLast);                 BOOL imageIsPng = hasAlpha;                  // But if we have an image data, we will look at the preffix                 if ([imageData length] >= [kPNGSignatureData length]) {                     imageIsPng = ImageDataHasPNGPreffix(imageData);                 }                  if (imageIsPng) {                     data = UIImagePNGRepresentation(image);                 }                 else {                     data = UIImageJPEGRepresentation(image, (CGFloat)1.0);                 }

磁盘缓存时,且需要recalculate时,判断png。两种方法判断png。

  • 是否有透明通道。alpha。
  • 是否有png的格式magic number前缀。

PS:如果需要加载gif,这里注意不要recalculate,否则 再次从磁盘加载后就不是gif文件了(变为jpg文件)。

27. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 276 - 278
  • Note :
    // this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance     // from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely.     exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]];

sharedManager 线程安全的哦

28. SDWebImageCompat.m

  • Path : /SDWebImage/SDWebImageCompat.m
  • Line : 15 - 49
  • Note :
inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image) {     if (!image) {         return nil;     }          if ([image.images count] > 0) {         NSMutableArray *scaledImages = [NSMutableArray array];          for (UIImage *tempImage in image.images) {             [scaledImages addObject:SDScaledImageForKey(key, tempImage)];         }          return [UIImage animatedImageWithImages:scaledImages duration:image.duration];     }     else {         if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {             CGFloat scale = [UIScreen mainScreen].scale;             if (key.length >= 8) {                 NSRange range = [key rangeOfString:@"@2x."];                 if (range.location != NSNotFound) {                     scale = 2.0;                 }                                  range = [key rangeOfString:@"@3x."];                 if (range.location != NSNotFound) {                     scale = 3.0;                 }             }              UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];             image = scaledImage;         }         return image;     } }

缩放图片。本地存储时,存储缩放的图片

29. NSData+ImageContentType.m

  • Path : /SDWebImage/NSData+ImageContentType.m
  • Line : 11 - 40
  • Note :
+ (NSString *)sd_contentTypeForImageData:(NSData *)data {     uint8_t c;     [data getBytes:&c length:1];     switch (c) {         case 0xFF:             return @"image/jpeg";         case 0x89:             return @"image/png";         case 0x47:             return @"image/gif";         case 0x49:         case 0x4D:             return @"image/tiff";         case 0x52:             // R as RIFF for WEBP             if ([data length] < 12) {                 return nil;             }              NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];             if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {                 return @"image/webp";             }              return nil;     }     return nil; }

从文件头判断图片类型

30. UIImage+GIF.m

  • Path : /SDWebImage/UIImage+GIF.m
  • Line : 14 - 57
  • Note :
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {     if (!data) {         return nil;     }      CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);      size_t count = CGImageSourceGetCount(source);      UIImage *animatedImage;      if (count <= 1) {         animatedImage = [[UIImage alloc] initWithData:data];     }     else {         NSMutableArray *images = [NSMutableArray array];          NSTimeInterval duration = 0.0f;          for (size_t i = 0; i < count; i++) {             CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);             if (!image) {                 continue;             }              duration += [self sd_frameDurationAtIndex:i source:source];              [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];              CGImageRelease(image);         }          if (!duration) {             duration = (1.0f / 10.0f) * count;         }          animatedImage = [UIImage animatedImageWithImages:images duration:duration];     }      CFRelease(source);      return animatedImage; }

使用CGImageSourceCreateWithData 读取Gif文件

31. SDWebImageDecoder.m

  • Path : /SDWebImage/SDWebImageDecoder.m
  • Line : 15 - 77
  • Note :
+ (UIImage *)decodedImageWithImage:(UIImage *)image {     // while downloading huge amount of images     // autorelease the bitmap context     // and all vars to help system to free memory     // when there are memory warning.     // on iOS7, do not forget to call     // [[SDImageCache sharedImageCache] clearMemory];      if (image == nil) { // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error         return nil;     }      @autoreleasepool{         // do not decode animated images         if (image.images) { return image; }                  CGImageRef imageRef = image.CGImage;                  CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);         BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||                          alpha == kCGImageAlphaLast ||                          alpha == kCGImageAlphaPremultipliedFirst ||                          alpha == kCGImageAlphaPremultipliedLast);                  if (anyAlpha) { return image; }                  // current         CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));         CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);                  bool unsupportedColorSpace = (imageColorSpaceModel == 0 || imageColorSpaceModel == -1 || imageColorSpaceModel == kCGColorSpaceModelCMYK || imageColorSpaceModel == kCGColorSpaceModelIndexed);         if (unsupportedColorSpace)             colorspaceRef = CGColorSpaceCreateDeviceRGB();                  size_t width = CGImageGetWidth(imageRef);         size_t height = CGImageGetHeight(imageRef);         NSUInteger bytesPerPixel = 4;         NSUInteger bytesPerRow = bytesPerPixel * width;         NSUInteger bitsPerComponent = 8;                  CGContextRef context = CGBitmapContextCreate(NULL,                                                      width,                                                      height,                                                      bitsPerComponent,                                                      bytesPerRow,                                                      colorspaceRef,                                                      kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);                  // Draw the image into the context and retrieve the new image, which will now have an alpha layer         CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);         CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(context);         UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha scale:image.scale orientation:image.imageOrientation];                  if (unsupportedColorSpace)             CGColorSpaceRelease(colorspaceRef);                  CGContextRelease(context);         CGImageRelease(imageRefWithAlpha);                  return imageWithAlpha;     } }

decode image,优化图片加载速度

32. SDImageCache.m

  • Path : /SDWebImage/SDImageCache.m
  • Line : 506 - 536
  • Note :
        NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];          // This enumerator prefetches useful properties for our cache files.         NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL                                                    includingPropertiesForKeys:resourceKeys                                                                       options:NSDirectoryEnumerationSkipsHiddenFiles                                                                  errorHandler:NULL];          NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];         NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];         NSUInteger currentCacheSize = 0;          // Enumerate all of the files in the cache directory.  This loop has two purposes:         //         //  1. Removing files that are older than the expiration date.         //  2. Storing file attributes for the size-based cleanup pass.         NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];         for (NSURL *fileURL in fileEnumerator) {             NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];              // Skip directories.             if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {                 continue;             }              // Remove files that are older than the expiration date;             NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];             if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {                 [urlsToDelete addObject:fileURL];                 continue;             }

利用文件系统存储文件的时间、大小等元数据来清理文件

33. NSFileManager.h

  • Path : /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSFileManager.h
  • Line : 442 - 442
  • Note :
@interface NSDictionary<KeyType, ObjectType> (NSFileAttributes)

KeyType 和 ObjectType 。Objective C还有这个语法呀。类似泛型。

34. NSDictionary.h

  • Path : /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSDictionary.h
  • Line : 14 - 14
  • Note :
@interface NSDictionary<__covariant KeyType, __covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

NSDictionary的声明。泛型中的协变和逆变。

参考这篇文章:http://blog.sunnyxx.com/2015/06/12/objc-new-features-in-2015/

https://msdn.microsoft.com/zh-cn/library/dd799517.aspx

35. SDWebImageManager.m

  • Path : /SDWebImage/SDWebImageManager.m
  • Line : 131 - 134
  • Note :
    BOOL isFailedUrl = NO;     @synchronized (self.failedURLs) {         isFailedUrl = [self.failedURLs containsObject:url];     }

存储失败的url

36. SDWebImageManager.m

  • Path : /SDWebImage/SDWebImageManager.m
  • Line : 119 - 126
  • Note :
    if ([url isKindOfClass:NSString.class]) {         url = [NSURL URLWithString:(NSString *)url];     }      // Prevents app crashing on argument type error like sending NSNull instead of NSURL     if (![url isKindOfClass:NSURL.class]) {         url = nil;     }

纠正参数类型。判断参数类型。NSNull。

37. SDWebImageManager.m

  • Path : /SDWebImage/SDWebImageManager.m
  • Line : 305 - 306
  • Note :
        NSArray *copiedOperations = [self.runningOperations copy];         [copiedOperations makeObjectsPerformSelector:@selector(cancel)];

NSArray竟然有这个方法,Objective C真的开发者友好啊。

38. UIImage+MultiFormat.m

  • Path : /SDWebImage/UIImage+MultiFormat.m
  • Line : 51 - 71
  • Note :
+(UIImageOrientation)sd_imageOrientationFromImageData:(NSData *)imageData {     UIImageOrientation result = UIImageOrientationUp;     CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);     if (imageSource) {         CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);         if (properties) {             CFTypeRef val;             int exifOrientation;             val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);             if (val) {                 CFNumberGetValue(val, kCFNumberIntType, &exifOrientation);                 result = [self sd_exifOrientationToiOSOrientation:exifOrientation];             } // else - if it's not set it remains at up             CFRelease((CFTypeRef) properties);         } else {             //NSLog(@"NO PROPERTIES, FAIL");         }         CFRelease(imageSource);     }     return result; }

从Data获取orientation

39. UIView+WebCacheOperation.m

  • Path : /SDWebImage/UIView+WebCacheOperation.m
  • Line : 16 - 24
  • Note :
- (NSMutableDictionary *)operationDictionary {     NSMutableDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);     if (operations) {         return operations;     }     operations = [NSMutableDictionary dictionary];     objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);     return operations; }

每个UIView关联一个operation集合。(主要用于在Cell重用,或者对UIImageView重复加载url image时,取消之前的加载任务)

40. UIView+WebCacheOperation.m

  • Path : /SDWebImage/UIView+WebCacheOperation.m
  • Line : 43 - 43
  • Note :
        } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){

conformsToProtocol:@protocol 学习了。

41. UIImageView+WebCache.m

  • Path : /SDWebImage/UIImageView+WebCache.m
  • Line : 174 - 177
  • Note :
- (void)addActivityIndicator {     if (!self.activityIndicator) {         self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self getIndicatorStyle]];         self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;

看来新版本SDWebImage集成了indicator功能。

42. UIImageView+WebCache.m

  • Path : /SDWebImage/UIImageView+WebCache.m
  • Line : 44 - 46
  • Note :
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {     [self sd_cancelCurrentImageLoad];     objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

cancel掉之前对当前imageview的加载操作。

Summarize

Generated by XSourceNote at 2016-04-03 07:11:39 +0000

原文  http://everettjf.github.io/2016/04/03/learn-sdwebimage
正文到此结束
Loading...