SDWebImage知识点总结一

2018-05-23  本文已影响35人  woniu

目不能两视而明,耳不能两听而聪。----------《荀子·劝学》

SDWebImage框架图

技术点归纳

与图片相关的,这些知识都是与图片相关的,无需一定要掌握。

1、PNG图片的判断。可以看SDImageCacheImageDataHasPNGPreffix方法。
2、Image Data判断图片类型以及根据data创建图片。可以查看NSData+ImageContentTypeUIImage+MultiFormat类。
3、图片解压(以及解压带来内存问题)。SDWebImageDecoder类中的decodedImageWithImage:方法的实现,牵扯到底层图片相关的操作。
4、gif图片的处理。虽然SDGIF的支持比较差劲。但是老外的纯技术精神,不得不佩服。请看issue#945

iOS开发技术,这些东西都是项目中比较常用的技术,一定要掌握。

1、NSFileManager的操作。在获取缓存大小相关内容时,需要我们熟练掌握NSFileManager类的相关用法。
2、NSCache类。在SDissue上面,由NSCache缓存引起的问题(如内存警告等)还是有很多的,后面才得到改善。
3、NSOperationNSOperationQueueNSThread@synchronized线程及操作队列相关类。
4、GCDdispatch_barrier_syncdispatch_apply等函数。
5、NSURLRequestNSURLResponseNSURLConnectionNSURLCache等网络请求相关类。
6、后台操作。
7、Runloop
8、Runtime
9、KVO
下面我们就主要的知识点来进行剖析:

一、SDWebImage中@autoreleasepool的应用

dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
            return;
        }

        @autoreleasepool {
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });
        }
    });
/*
@autoreleasepool {
    // code do something, creates some autoreleases objects
}
*/

1、autoreleasepool在什么时候使用?

for (int i = 0; i < 1000000; i++) {
        @autoreleasepool {
            NSString *str = @"abc";
            str = [str stringByAppendingString:@"zhangsan"];
        }        
}

下面的方法中,对象不断被创建,在for循环结束之前,这些对象都会被系统放到最近的自动释放池里面,等待回收,因此就会消耗大量的内存,可能造成内存枯竭。而等到循环结束之后,内存的用量会突然下降。
而如果把循环内的代码包裹在我们创建的@autoreleasepool {}中,那么在循环中创建的对象就会放到这个池子中,而不是在线程的主池里面。@autoreleasepool {}的作用范围是在{}内部,所以创建的对象会得到及时的释放,防止内存的暴涨。

for (int i = 0; i < 10000; i++) {
   NSString *str = @"abc";
}
扩展:
1、在什么线程下面需要使用autoreleasepool呢?
 dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
            return;
        }
        @autoreleasepool {
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }
            dispatch_async(dispatch_get_main_queue(), ^{
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });
        }
    });
2、autorelease的对象何时被释放?

2、autoreleasepool原理是什么?

AutoreleasePool(自动释放池)是OC中的一种内存自动回收机制,它可以延迟加入AutoreleasePool中的变量release的时机。在正常情况下,创建的变量会在超出其作用域的时候release,但是如果将变量加入AutoreleasePool,那么release将延迟执行。

自动释放池的执行过程:objc_autoreleasePoolPush() —> [object autorelease] —> objc_autoreleasePoolPop(void *)。

int main(int argc, const char * argv[]) {

    /* @autoreleasepool */ {
        void *atautoreleasepoolobj = objc_autoreleasePoolPush();
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_kb_06b822gn59df4d1zt99361xw0000gn_T_main_d39a79_mi_0);
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }

    return 0;
}
每一个线程都会维护自己的 autoreleasepool 堆栈。换句话说 autoreleasepool 是与线程紧密相关的,每一个 autoreleasepool 只对应一个线程,子线程退出的时候会清空autoreleasepool。
tips:子线程的autoreleasepool也需要手动获取,但区分情况,一般系统提供的block如usingBlock和GCD提供的block内部都会自动包裹一个autoreleasepool,不用手动加。但是你自己通过其他方式创建的子线程,在线程内部需要手动获取autoreleasepool,防止局部内存使用峰值过高或发生其他内存问题,最后,autoreleasepool释放时,也会对其管理的对象发送release消息。

MarkDown语法扩展

二、SDWebImage中的@synchronized(同步锁)

//SDWebImageDownloaderOperation中取消线程的执行
- (void)cancel {
    @synchronized (self) {
        if (self.thread) {
            [self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
        }
        else {
            [self cancelInternal];
        }
    }
}
@synchronized (tokenA) {
    [arrA addObject:obj];
}

@synchronized (tokenB) {
    [arrB addObject:obj];
}

三、SDWebImage的内存警告处理

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(clearMemory)
                                             name:UIApplicationDidReceiveMemoryWarningNotification
                                           object:nil];
@property (strong, nonatomic) NSCache *memCache;

- (void)clearMemory {
    [self.memCache removeAllObjects];
}
知识点扩展NSCache简介:

四、SDWebImage的Disk缓存时长、清理操作时间点以及清理原则。

typedef NS_ENUM(NSInteger, SDImageCacheType) {
    /**
     * The image wasn't available the SDWebImage caches, but was downloaded from the web.
     */
    SDImageCacheTypeNone,
    /**
     * The image was obtained from the disk cache.
     */
    SDImageCacheTypeDisk,
    /**
     * The image was obtained from the memory cache.
     */
    SDImageCacheTypeMemory
};
//SDImageCache.m
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
//SDImageCache.m
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(deleteOldFiles)
                                             name:UIApplicationWillTerminateNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(backgroundDeleteOldFiles)
                                             name:UIApplicationDidEnterBackgroundNotification
                                           object:nil];

由于进入后台之后执行清除数据操作,但是应用在很短时间内就会被挂起导致操作无法完成,所及就涉及到申请后台运行时间来执行操作的问题了。

- (void)backgroundCleanDisk {
    Class UIApplicationClass = NSClassFromString(@"UIApplication");
    if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
        return;
    }
    UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
// 后台任务标识--注册一个后台任务
    __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        // Clean up any unfinished task business by marking where you
        // stopped or ending the task outright.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    [self cleanDiskWithCompletionBlock:^{
        //结束后台任务
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
}

申请后台运行时间在iOS 7.0之前是600s,在iOS 7.0之后就变成180s了,但是这些时间已经足够我们完成一般的操作了。UIBackgroundTaskIdentifier知识点传送门

扩展知识点:
1、static const与#define(宏定义)

之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量,那么为什么呢?。

五、SDWebImage存储在那里嫩?

- (id)init {
    return [self initWithNamespace:@"default"];
}

- (id)initWithNamespace:(NSString *)ns {
    NSString *path = [self makeDiskCachePath:ns];
    return [self initWithNamespace:ns diskCacheDirectory:path];
}

- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory {
    if ((self = [super init])) {
        NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];

        // initialise PNG signature data
        kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];

        // Create IO serial queue
        _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);

        // Init default values
        _maxCacheAge = kDefaultCacheMaxCacheAge;

        //......
    return self;
}

六、SDWebImage用到的回调设计

typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType);

typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);

typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
/**
 * Remove all expired cached image from disk. Non-blocking method - returns immediately.
 * @param completionBlock An block that should be executed after cache expiration completes (optional)
 */
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock;

/**
 * Asynchronously calculate the disk cache's size.
 */
- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock;

/**
 *  Async check if image exists in disk cache already (does not load the image)
 *
 *  @param key             the key describing the url
 *  @param completionBlock the block to be executed when the check is done.
 *  @note the completion block will be always executed on the main queue
 */
- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;

@protocol SDWebImagePrefetcherDelegate <NSObject>

@optional

/**
 * Called when an image was prefetched.
 *
 * @param imagePrefetcher The current image prefetcher
 * @param imageURL        The image url that was prefetched
 * @param finishedCount   The total number of images that were prefetched (successful or not)
 * @param totalCount      The total number of images that were to be prefetched
 */
- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;

/**
 * Called when all images are prefetched.
 * @param imagePrefetcher The current image prefetcher
 * @param totalCount      The total number of images that were prefetched (whether successful or not)
 * @param skippedCount    The total number of images that were skipped
 */
- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;

@end

七、SDWebImage中用到的枚举

doneBlock(diskImage, SDImageCacheTypeDisk);
[gifImageView sd_setImageWithURL:url placeholderImage:image options:SDWebImageRefreshCached | SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {

            } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
 
            }];

SDWebImage示例如下:

typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
    SDWebImageDownloaderLowPriority = 1 << 0,     // 值为2的0次方

    SDWebImageDownloaderProgressiveDownload = 1 << 1,  // 值为2的1次方

    SDWebImageDownloaderUseNSURLCache = 1 << 2,

    SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,

    SDWebImageDownloaderContinueInBackground = 1 << 4,

    SDWebImageDownloaderHandleCookies = 1 << 5,

    SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,

    SDWebImageDownloaderHighPriority = 1 << 7,
};
typedef NS_ENUM(NSInteger, SDImageCacheType) {
    /**
     * The image wasn't available the SDWebImage caches, but was downloaded from the web.
     */
    SDImageCacheTypeNone,
    /**
     * The image was obtained from the disk cache.
     */
    SDImageCacheTypeDisk,
    /**
     * The image was obtained from the memory cache.
     */
    SDImageCacheTypeMemory
};

SDWebImage源码链接
参考资料1
SDWebImage4.0源码探究1
SDWebImage4.0源码探究2
autoreleasepool使用
AutoreleasePool的原理和实现

上一篇下一篇

猜你喜欢

热点阅读