SDWebImage 詳解

SDWebImage 框架中使用的知識(shí)點(diǎn)整理

<1>.NSCache
蘋果官方文檔中介紹NSCache是一種可變集合,用于臨時(shí)存儲(chǔ)在資源不足時(shí)可能被釋放的鍵值對(duì)。
和其他可變集合不同之處有:

1.NSCache在系統(tǒng)內(nèi)存很低時(shí),會(huì)自動(dòng)釋放一些對(duì)象(而且是沒(méi)有順序的,所以SDWebImage中還使用了NSMapTable作為緩存的備份,
當(dāng)在NSCache找不到時(shí),再去NSMapTable中查找)。
2.NSCache是線程安全的,所以SDWebImage中NSCache做增刪操作沒(méi)有加鎖。
3.與NSMutableDictionary對(duì)象不同,緩存不會(huì)復(fù)制放入其中的鍵對(duì)象。

在SDWebImage源碼文件SDImageCache.m 中,使用NSCache做內(nèi)存緩存。

// A memory cache which auto purge the cache on memory warning and support weak cache.
@interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType>

@end

// Private
@interface SDMemoryCache <KeyType, ObjectType> ()

@property (nonatomic, strong, nonnull) SDImageCacheConfig *config;
@property (nonatomic, strong, nonnull) NSMapTable<KeyType, ObjectType> *weakCache; // strong-weak cache
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock; // a lock to keep the access to `weakCache` thread-safe

- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfig:(nonnull SDImageCacheConfig *)config;

@end

其中weakCache就是上文說(shuō)的用來(lái)做補(bǔ)充的緩存集合
通過(guò)重寫這四個(gè)方法,同步對(duì)weakCache做增刪操作

- (void)setObject:(ObjectType)obj forKey:(KeyType)key; // 0 cost
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
- (void)removeObjectForKey:(KeyType)key;

- (void)removeAllObjects;

為什么用NSMapTable做備份的原因上面已經(jīng)說(shuō)過(guò)了,可為什么不直接使用NSMapTable,而要使用NSCache?除了上面說(shuō)的線程安全,內(nèi)存緊張時(shí)會(huì)自動(dòng)釋放存儲(chǔ)對(duì)象,NSCache還有totalCostLimit和countLimit兩個(gè)屬性控制消耗限制和數(shù)量限制。

<2>. @autoreleasepool

自動(dòng)釋放池塊提供了一種機(jī)制,你放棄對(duì)象的所有權(quán)的同時(shí),可以避免立即釋放它(例如從方法返回對(duì)象時(shí))。Application Kit在事件循環(huán)的每個(gè)循環(huán)開始時(shí)在主線程上創(chuàng)建一個(gè)自動(dòng)釋放池,并在最后將其排出,從而釋放處理事件時(shí)生成的任何自動(dòng)釋放的對(duì)象。每個(gè)線程(包括主線程)都維護(hù)自己的NSAutoreleasePool對(duì)象堆棧。在創(chuàng)建新池時(shí),它們會(huì)添加到堆棧頂部。當(dāng)池被釋放時(shí),它們將從堆棧中刪除。自動(dòng)釋放的對(duì)象放置在當(dāng)前線程的頂部自動(dòng)釋放池中。當(dāng)線程終止時(shí),它會(huì)自動(dòng)排出與自身關(guān)聯(lián)的所有自動(dòng)釋放池。通常情況下你不需要?jiǎng)?chuàng)建自己的釋放池,除非應(yīng)用程序在事件循環(huán)中創(chuàng)建了大量臨時(shí)自動(dòng)釋放的對(duì)象,則創(chuàng)建“本地”自動(dòng)釋放池以幫助最小化峰值內(nèi)存占用量。

蘋果開發(fā)文檔中實(shí)例

NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
 
    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url
                                         encoding:NSUTF8StringEncoding error:&error];
        /* Process the string, creating and autoreleasing more objects. */
    }
}

如果必須使用自動(dòng)釋放池塊之外的臨時(shí)對(duì)象,則可以通過(guò)向塊內(nèi)的對(duì)象發(fā)送保留消息,然后在塊之后將其發(fā)送自動(dòng)釋放,如此示例所示:

– (id)findMatchingObject:(id)anObject {
   id match;
   while (match == nil) {
       @autoreleasepool {
           /* Do a search that creates a lot of temporary objects. */
           match = [self expensiveSearchForObject:anObject];
           if (match != nil) {
               [match retain]; /* Keep match around. */
           }
       }
   }
   return [match autorelease];   /* Let match go and return it. */
}

發(fā)送retain以在自動(dòng)釋放池中匹配并在自動(dòng)釋放池塊延長(zhǎng)匹配的生命周期后向其發(fā)送自動(dòng)釋放,并允許它在循環(huán)外接收消息并返回到findMatchingObject:的調(diào)用者。

實(shí)例:

源碼 SDWebImageCoderHelper.m
for (size_t i = 0; i < frameCount; i++) {
        @autoreleasepool {
            SDWebImageFrame *frame = frames[i];
            float frameDuration = frame.duration;
            CGImageRef frameImageRef = frame.image.CGImage;
            NSDictionary *frameProperties = @{(__bridge NSString *)kCGImagePropertyGIFDictionary : @{(__bridge NSString *)kCGImagePropertyGIFDelayTime : @(frameDuration)}};
            CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties);
        }
    }

使用Clang將main.m轉(zhuǎn)換為main.cpp文件
終端輸入 clang -rewrite-objc main.m ,得到__AtAutoreleasePool的結(jié)構(gòu)

extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

從 objc_autoreleasePoolPush、objc_autoreleasePoolPop可以看出來(lái)是堆棧進(jìn)棧出棧的操作。高頻率的創(chuàng)建變量后,加入自動(dòng)釋放池,可以避免短時(shí)間之內(nèi)存暴漲

 @autoreleasepool {
        //進(jìn)棧
        __AtAutoreleasePool __autoreleasepool = objc_autoreleasePoolPush();

       // 新建對(duì)象加入自動(dòng)釋放池
        
        //出棧
        objc_autoreleasePoolPop(__autoreleasepool)
}

<3>.CGImageSourceRef

<4>.NSOperation,NSOperationQueue

<5>.dispatch_semaphore_t

源碼 SDWebImageDownloader.m 
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);

@property (strong, nonatomic, nonnull) dispatch_semaphore_t operationsLock; // a lock to keep the access to `URLOperations` thread-safe
@property (strong, nonatomic, nonnull) dispatch_semaphore_t headersLock; // a lock to keep the access to `HTTPHeaders` thread-safe

_operationsLock = dispatch_semaphore_create(1);
_headersLock = dispatch_semaphore_create(1);

__weak typeof(self) wself = self;
operation.completionBlock = ^{
            __strong typeof(wself) sself = wself;
            if (!sself) {
                return;
            }
            LOCK(sself.operationsLock);
            [sself.URLOperations removeObjectForKey:url];
            UNLOCK(sself.operationsLock);
 };        
- (void)cancel {
    @synchronized(self) {
        self.cancelled = YES;
        if (self.cacheOperation) {
            [self.cacheOperation cancel];
            self.cacheOperation = nil;
        }
        if (self.downloadToken) {
            [self.manager.imageDownloader cancel:self.downloadToken];
        }
        [self.manager safelyRemoveOperationFromRunning:self];
    }
}

<6>.dispatch_main_async_safe

#ifndef dispatch_queue_async_safe
#define dispatch_queue_async_safe(queue, block)\
    if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(queue)) {\
        block();\
    } else {\
        dispatch_async(queue, block);\
    }
#endif

#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block) dispatch_queue_async_safe(dispatch_get_main_queue(), block)
#endif



<7>.
__weak typeof(self)weakSelf = self;
__strong typeof(weakSelf)strongSelf = weakSelf;
if (!strongSelf) {
   return;
}                          

<8>.Objective-C Associated Objects

Associated Objects 主要用來(lái)給分類增加屬性

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key);

從名字可以看出兩個(gè)方法分別用于實(shí)現(xiàn)屬性的set和get。
objc_setAssociatedObject 用于給對(duì)象添加屬性,傳入 nil 可以移除已添加的屬性;
objc_getAssociatedObject 用于獲取屬性;

分類 UIImage + FLAnimatedImage 中源碼
- (FLAnimatedImage *)sd_FLAnimatedImage {
    return objc_getAssociatedObject(self, @selector(sd_FLAnimatedImage));
}

- (void)setSd_FLAnimatedImage:(FLAnimatedImage *)sd_FLAnimatedImage {
    objc_setAssociatedObject(self, @selector(sd_FLAnimatedImage), sd_FLAnimatedImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

其中@selector(sd_FLAnimatedImage) 就是方法參數(shù)中的key,SDWebImage 有些也使用類似 static char sd_FLAnimatedImage,兩種方式都可行,但是還是推薦使用@selector(),這樣不用另外在其他申明參數(shù),保持代碼命名的一致性。(其中objc_setAssociatedObject ()還可以使用_cmd作為key)

其中OBJC_ASSOCIATION_RETAIN_NONATOMIC 就是方法參數(shù)中的objc_AssociationPolicy policy

Behavior @property Equivalent Description
OBJC_ASSOCIATION_ASSIGN @property (assign) or @property (unsafe_unretained) Specifies a weak reference to the associated
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong) Specifies a strong reference to the associated object.
OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) Specifies that the associated object is copied.
OBJC_ASSOCIATION_RETAIN @property (atomic, strong) pecifies a strong reference to the associated object.
OBJC_ASSOCIATION_COPY @property (atomic, copy) Specifies that the associated object is copied.

Mattt大神 文章 associated-objects


iOS 動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)的區(qū)別 framework


dispatch_group_enter和dispatch_group_leave

FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStartNotification;
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStopNotification;
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadFinishNotification;


NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification";
NSString *const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDownloadReceiveResponseNotification";
NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification";


static inline





最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 目的 在使用SDWebImage加載圖片時(shí),尤其是加載gif等大圖時(shí),SDWebImage會(huì)將圖片緩存在內(nèi)存中,這...
    Swift社區(qū)閱讀 3,192評(píng)論 2 5
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,229評(píng)論 8 265
  • 一. SDWebImage介紹 SDWebImage 是目前最流行、使用最廣泛的第三方圖片處理框架,它不僅能夠異步...
    jmyywm閱讀 4,800評(píng)論 0 2
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,051評(píng)論 0 9
  • 1.異步方式下載網(wǎng)絡(luò)圖片,使用memory+disk來(lái)緩存網(wǎng)絡(luò)圖片,自動(dòng)管理緩存。 2.耗時(shí)操作都在子線程,確保不...
    keenteam閱讀 602評(píng)論 0 2

友情鏈接更多精彩內(nèi)容