SDWebImage note

Basic Information

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