從SDWebImage原始碼中學到的
親,我的簡書已不再維護和更新了,所有文章都遷移到了我的個人部落格:https://mikefighting.github.io/,歡迎交流。
保證一段程式碼在主執行緒中執行,怎麼做更好?
可以使用一個巨集來替代,這樣程式碼更加整潔,如
#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);\
}
Block除了常見的回撥,還有什麼應用場景?
在具體的處理方式需要客戶端傳入時。(在模板方法設計模式中,其呼叫的是子類的實現)。
如:
typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);
@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
該處就是利用Block將處理CacheKey的方法開放給了客戶端。通過block的返回值獲取。其實這裡用代理也可以實現,但是代理相對來說程式碼量會更多,並且程式碼較為分散。
我們在加鎖的時候一直用@synchronized (self)合理嗎?
不合理,@synchronized (objc),只要這個objc是同一個物件,那麼就會獲得同一把鎖。如果訪問的是兩種不同的資源,那麼就需要使用兩種不同的objc,比如:
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
@synchronized (self.failedURLs) {
isFailedUrl = [self.failedURLs containsObject:url];
}
這裡分別是對runningOperations
和failedURLs
同步,那麼就需要使用兩種不同的objc,當然都用self
不能算錯,但是將不需要同步的程式碼同步了,就降低了系統的效能。另外使用self,還容易引起死鎖,比如下程式碼:
//class A
@synchronized (self) {
[_sharedLock lock];
NSLog(@"code in class A");
[_sharedLock unlock];
}
//class B
[_sharedLock lock];
@synchronized (objectA) {
NSLog(@"code in class B");
}
[_sharedLock unlock];
如果要讓陣列中的每個物件都呼叫某個方法怎麼做?
- (void)makeObjectsPerformSelector:(SEL)aSelector`
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(nullable id)argument`
以上兩個方法可以實現,而不需要分別遍歷每個物件,然後分別呼叫performSelector:
記憶體快取為啥要用NSCache
?
NSCache和NSDictionary極其相似,他的方法如下:
- (nullable ObjectType)objectForKey:(KeyType)key;
- (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;
@property NSUInteger totalCostLimit; // limits are imprecise/not strict
@property NSUInteger countLimit; // limits are imprecise/not strict
@property BOOL evictsObjectsWithDiscardedContent;
從中可以看到,他和NSDictioary非常相似,但是為啥在做記憶體快取時要用它呢?原因在於:
- 當系統資源將要耗盡時,它可以自動刪減快取。如果採用普通的字典,那麼就要自己編寫掛鉤,在系統發出“低記憶體”通知時手工刪減快取。
- NSCache還是執行緒安全的。
- NSDictionary中的key必須實現NSCopying協議,NSCache中的key不必實現copy因為它是"保留"鍵的(強引用),而不是"拷貝"鍵的。
- 如果快取設定超過了設定的最大值,則會清除舊的資料,保留最新快取的資料。
FOUNDATION_STATIC_INLINE放在方法名前有何作用?
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
return image.size.height * image.size.width * image.scale * image.scale;
}
行內函數的程式碼會被直接嵌入在它被呼叫的地方,呼叫幾次就嵌入幾次,沒有使用call指令。這樣省去了函式呼叫時的一些額外開銷,比如儲存和恢復函式返回地址等,可以加快速度。不過呼叫次數多的話,會使可執行檔案變大,這樣會降低速度。相比起巨集來說,核心開發者一般更喜歡使用行內函數。因為行內函數沒有長度限制,格式限制。編譯器還可以檢查函式呼叫方式,以防止其被誤用。
inline
(行內函數)在什麼時候使用?
在SDWebIamgeCompat
中使用了inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image)
,這是個行內函數(函式程式碼被放入符號表中,在使用時進行替換,比呼叫一般的函式更加高效),那麼我們在什麼時候使用行內函數呢?經過查詢相關資料,總結下inline的使用場合:
使用inline的場合:
- 想要使用
inline
替換#define
時。 - 函式很短。(如果函式的程式碼較長,使用內聯將消耗過多記憶體)
- 函式呼叫很頻繁。
不應使用inline
的場合: - 很大的函式。
- 和I/O相關的函式。
- 建構函式和解構函式。
- 開放框架時候,使用
inline
可能會破壞框架的相容性。
獲取某個目錄下檔案的個數:
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
count = [[fileEnumerator allObjects] count];
如何讓某個屬性只在固定版本的時候才會有?
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
#endif
上面這段程式碼就可以讓backgroundTaskId在iphone的版本在4.0以後才會有。
Notification的方法呼叫所在的執行緒是根據Post
時候所線上程決定的,
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
});
這樣註冊該通知的物件就可以在主執行緒中呼叫響應的方法了。
NSRunLoop,為了保持後臺下載圖片的執行緒一直存在,就使用了RunLoop
CFRunLoopRun()
和CFRunLoopStop(CFRunLoopGetCurrent())
分別用來開始和結束一個RunLoop
分類中需要填加屬性怎麼辦?
如果分類中的屬性只是分類內部使用,那麼其實可以直接使用關聯,而不必非要顯式建立一個屬性,這樣也可以直接使用.
語法,這時沒有屬性,所以.
語法的無論是在=
左邊,還是在=
右邊最終都會呼叫這個方法,例如:
static char imageURLStorageKey;
- (NSMutableDictionary *)imageURLStorage {
NSMutableDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
if (!storage)
{
storage = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return storage;
}
如何讓一個id
型別的物件呼叫某個具體的方法?
讓這個物件遵守某項協議就可以呼叫,如下
for (id <SDWebImageOperation> operation in operations) {
if (operation) {
[operation cancel];
}
同理,如果想讓某個方法的返回值具有某個方法,也可以讓這個返回值遵守某協議,如:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress: (SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;
設計介面時,要儘量考慮使用者的習慣,並對常見錯誤進行處理
在需要傳入URL引數的地方,使用者很可能不小心傳入了字串,這個時候要麼在方法中丟擲異常,要麼就在內部判斷型別並替使用者做相應的轉換,如:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url {
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
}
未完,待續
相關文章
- 原始碼閱讀:SDWebImage(一)——從使用入手原始碼Web
- SDWebImage(v3.7.6) 原始碼學習Web原始碼
- SDWebImage 原始碼分析Web原始碼
- SDWebImage原始碼解析Web原始碼
- SDWebImage 原始碼解析Web原始碼
- SDWebImage原始碼剖析(-)Web原始碼
- 「從原始碼中學習」Vue原始碼中的JS騷操作原始碼VueJS
- SDWebImage原始碼解讀Web原始碼
- SDWebImage原始碼解析(三)Web原始碼
- SDWebImage原始碼解析(四)Web原始碼
- SDWebImage原始碼剖析(二)Web原始碼
- [譯] 我們能從 Redux 原始碼中學到什麼?Redux原始碼
- 【分享】從Mybatis原始碼中,學習到的10種設計模式MyBatis原始碼設計模式
- 「讀懂原始碼系列2」我從 lodash 原始碼中學到的幾個知識點原始碼
- SDWebImage原始碼閱讀(上)Web原始碼
- SDWebImage使用及原始碼分析Web原始碼
- 從教女友寫程式碼中學到的
- mybatis原始碼學習:從SqlSessionFactory到代理物件的生成MyBatis原始碼SQLSession物件
- SDWebImage原始碼解析之SDWebImageManager的註解Web原始碼
- 通俗易懂的SDWebImage原始碼解析(二)Web原始碼
- 原始碼閱讀:SDWebImage(十一)——SDImageCache原始碼Web
- 原始碼閱讀:SDWebImage(五)——SDWebImageFrame原始碼Web
- 原始碼閱讀:SDWebImage(十六)——SDWebImageTransition原始碼Web
- 原始碼閱讀:SDWebImage(十四)——SDWebImageManager原始碼Web
- 從bootstrap原始碼中學習Sass(一)boot原始碼
- WebViewJavascriptBridge 原始碼中 Get 到的“橋樑美學”WebViewJavaScript原始碼
- 原始碼閱讀:SDWebImage(八)——SDWebImageGIFCoder原始碼Web
- 原始碼閱讀:SDWebImage(九)——SDWebImageCodersManager原始碼Web
- 原始碼閱讀:SDWebImage(六)——SDWebImageCoderHelper原始碼Web
- 原始碼閱讀:SDWebImage(四)——SDWebImageCoder原始碼Web
- 原始碼閱讀:SDWebImage(二)——SDWebImageCompat原始碼Web
- 原始碼閱讀:SDWebImage(七)——SDWebImageImageIOCoder原始碼Web
- 原始碼閱讀:SDWebImage(十三)——SDWebImageDownloader原始碼Web
- 原始碼閱讀:SDWebImage(十五)——SDWebImagePrefetcher原始碼Web
- 原始碼閱讀:SDWebImage(十二)——SDWebImageDownloaderOperation原始碼Web
- 我從其他人的Shell指令碼中學到的指令碼
- 從 Aspects 原始碼中我學到了什麼?原始碼
- 我從Superfish事件中學到的事件