YYWebImage 學(xué)習(xí)(二)

繼續(xù)上一篇的學(xué)習(xí)风科,本篇著重學(xué)習(xí)對YYWebImageManager浅萧、YYWebImageOperation 進(jìn)一步了解穿剖,還是以問題的方式帶入。

* NSMutableURLRequest處對request 做了什么特別處理欧穴?
* 為什么很多地方使用 @autoreleasepool {}
* 重寫里面的一些方法民逼,做了什么處理?
* 鎖和線程中在里面的運(yùn)用涮帘?
* NSURLConnectionDelegate代理方法中有沒有一些特別的處理拼苍?
* 此處用的是蘋果已經(jīng)棄用的NSURLConnection,是否后期會替換调缨?
1疮鲫、NSMutableURLRequest處對request 做了什么處理?
 request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ?
        NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;

設(shè)置緩存策略,對加載圖片模式進(jìn)行處理弦叶。

  • NSURLRequestUseProtocolCachePolicy這個是系統(tǒng)默認(rèn)的緩存策略,緩存不存在,就去重新服務(wù)端拉去,如果存在的話,根據(jù)下一步請求的Cache-control字段來進(jìn)行下一步的操作,比如如果cache-control = must-revalidata,那么還會去詢問服務(wù)端是否有數(shù)據(jù)更新,有的話就拉取新數(shù)據(jù),沒有就返回緩存
  • NSURLRequestReloadIgnoringLocalCacheData:忽略本地緩存,每次都去請求服務(wù)端

當(dāng)然生成一個YYWebImageOperation對象俊犯,才是其核心咯。

2伤哺、為什么很多地方使用使用 @autoreleasepool {}?

通過Objective-C Autorelease Pool 的實(shí)現(xiàn)原理黑幕背后的Autorelease的了解燕侠,一般我們在下列情況會用到@autoreleasepool {}。

  • 如果你編寫的程序不是基于 UI 框架的默责,比如說命令行工具贬循;
  • 如果你編寫的循環(huán)中創(chuàng)建了大量的臨時對象咸包;
  • 如果你創(chuàng)建了一個輔助線程桃序。

我的理解是作者在很多方法里面都用到了它,是為了可以更好的管理內(nèi)存這塊不會出問題烂瘫,讓產(chǎn)生的對象都能在適當(dāng)?shù)牡胤结尫拧?/p>

PS:@ autorelease基本注意點(diǎn):

  • 將對象放到一個自動釋放池中媒熊,結(jié)束后對象會返回對象本身奇适;
  • 當(dāng)自動釋放池被銷毀時, 會對其池子里面的所有對象做一次release 操作芦鳍。
  • 占用內(nèi)存較大的對象不要隨便用 autorelease嚷往;占用內(nèi)存較小的對象使用 autorelease,沒有太大影響柠衅;
  • 系統(tǒng)自帶方法里面沒有alloc皮仁、new、copy菲宴,說明返回的對象是autorelease的贷祈。
3、重寫里面的一些方法喝峦,做了什么處理势誊?

以 start 舉例, @autoreleasepool谣蠢,遞歸鎖防止死鎖粟耻,_cancelOperation方法的使用,但最核心的當(dāng)然還是依據(jù)各種條件狀態(tài)進(jìn)行調(diào)整眉踱。

- (void)start {
    @autoreleasepool {
        [_lock lock];
        self.started = YES;
        if ([self isCancelled]) {
            [self performSelector:@selector(_cancelOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
            self.finished = YES;
        } else if ([self isReady] && ![self isFinished] && ![self isExecuting]) {
            if (!_request) {
                self.finished = YES;
                if (_completion) {
                    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey:@"request in nil"}];
                    _completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageFinished, error);
                }
            } else {
                self.executing = YES;
                [self performSelector:@selector(_startOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
                if ((_options & YYWebImageOptionAllowBackgroundTask) && _YYSharedApplication()) {
                    __weak __typeof__ (self) _self = self;
                    if (_taskID == UIBackgroundTaskInvalid) {
                        _taskID = [_YYSharedApplication() beginBackgroundTaskWithExpirationHandler:^{
                            __strong __typeof (_self) self = _self;
                            if (self) {
                                [self cancel];
                                self.finished = YES;
                            }
                        }];
                    }
                }
            }
        }
        [_lock unlock];
    }
}

通過給出的條件定制自己的 Operation挤忙,此時回過去看看 _startOperation

- (void)_startOperation {
    if ([self isCancelled]) return;
    @autoreleasepool {
        // 如果緩存存在,并且不等于使用NSURLCache,并且不是刷新緩存,則直接通過_cacheKey 獲取
        if (_cache &&
            !(_options & YYWebImageOptionUseNSURLCache) &&
            !(_options & YYWebImageOptionRefreshImageCache)) {
            UIImage *image = [_cache getImageForKey:_cacheKey withType:YYImageCacheTypeMemory];
            if (image) {
                // 得到圖片
                [_lock lock];
                if (![self isCancelled]) {
                    if (_completion) _completion(image, _request.URL, YYWebImageFromMemoryCache, YYWebImageStageFinished, nil);
                }
                [self _finish];
                [_lock unlock];
                return;
            }
            // 判斷下載模式
            if (!(_options & YYWebImageOptionIgnoreDiskCache)) {
                __weak typeof(self) _self = self;
                dispatch_async([self.class _imageQueue], ^{
                    __strong typeof(_self) self = _self;
                    if (!self || [self isCancelled]) return;
                    UIImage *image = [self.cache getImageForKey:self.cacheKey withType:YYImageCacheTypeDisk];
                    if (image) {
                        [self.cache setImage:image imageData:nil forKey:self.cacheKey withType:YYImageCacheTypeMemory];
                        [self performSelector:@selector(_didReceiveImageFromDiskCache:) onThread:[self.class _networkThread] withObject:image waitUntilDone:NO];
                    } else {
                         // 假如沒有圖片就到網(wǎng)絡(luò)線程立刻開始請求
                        [self performSelector:@selector(_startRequest:) onThread:[self.class _networkThread] withObject:nil waitUntilDone:NO];
                    }
                });
                return;
            }
        }
    }
    //在網(wǎng)絡(luò)線程立刻開始請求
    [self performSelector:@selector(_startRequest:) onThread:[self.class _networkThread] withObject:nil waitUntilDone:NO];
}

總的說來谈喳,在重寫系統(tǒng)方法的時候饭玲,就是根據(jù)自己的條件定制化自己的 operation。

4叁执、鎖和線程中在里面的運(yùn)用茄厘?

4-1、互斥鎖的使用

[lock lock];    // 上鎖
// 處理公共資源
[self dosomething]
[lock unlock];  // 釋放鎖

以及@synchronized互斥鎖簡潔版本的使用谈宛。很直接的說明次哈,用來保護(hù)同一時間只有一個線程訪問數(shù)據(jù)。

4-2吆录、全局隊(duì)列

+ (dispatch_queue_t)_imageQueue {
    #define MAX_QUEUE_COUNT 16
    static int queueCount;
    static dispatch_queue_t queues[MAX_QUEUE_COUNT];
    static dispatch_once_t onceToken;
    static int32_t counter = 0;
    dispatch_once(&onceToken, ^{
        queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
        queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
        if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
            for (NSUInteger i = 0; i < queueCount; i++) {
                dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
                queues[i] = dispatch_queue_create("com.ibireme.image.decode", attr);
            }
        } else {
            for (NSUInteger i = 0; i < queueCount; i++) {
                queues[i] = dispatch_queue_create("com.ibireme.image.decode", DISPATCH_QUEUE_SERIAL);
                dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
            }
        }
    });
    int32_t cur = OSAtomicIncrement32(&counter);
    if (cur < 0) cur = -cur;
    return queues[(cur) % queueCount];
    #undef MAX_QUEUE_COUNT
}
dispatch_async([self.class _imageQueue], ^{
         //  do something  
 });

全局圖片線程,用于讀取 窑滞、解碼圖片,注意最大線程數(shù)的限制,以及iOS 8之后喜爷,創(chuàng)建 queues 的不同府蔗。

  • dispatch_queue_attr_make_with_qos_class
    這個函數(shù)可以創(chuàng)建帶有優(yōu)先級的dispatch_queue_attr_t對象。通過這個對象可以自定義queue的優(yōu)先級此改。
  • dispatch_set_target_queue
    可以設(shè)置優(yōu)先級,也可以設(shè)置隊(duì)列層級體系侄柔,比如讓多個串行和并行隊(duì)列在統(tǒng)一一個串行隊(duì)列里串行執(zhí)行
5共啃、NSURLConnectionDelegate代理方法中有沒有一些特別的處理占调?

特別要注意一個地方,接收到數(shù)據(jù)對進(jìn)度的展示

//收到數(shù)據(jù)回調(diào)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data ;

    /*---------- progressive ----------------------------*/
    BOOL progressive = (_options & YYWebImageOptionProgressive) > 0;
    BOOL progressiveBlur = (_options & YYWebImageOptionProgressiveBlur) > 0;
    if (!_completion || !(progressive || progressiveBlur)) return;
    if (data.length <= 16) return;
    if (_expectedSize > 0 && data.length >= _expectedSize * 0.99) return;
    if (_progressiveIgnored) return;
    
    NSTimeInterval min = progressiveBlur ? MIN_PROGRESSIVE_BLUR_TIME_INTERVAL : MIN_PROGRESSIVE_TIME_INTERVAL;
    NSTimeInterval now = CACurrentMediaTime();
    if (now - _lastProgressiveDecodeTimestamp < min) return;
    //沒有解碼,初始化一個解碼器
    if (!_progressiveDecoder) {
        _progressiveDecoder = [[YYImageDecoder alloc] initWithScale:[UIScreen mainScreen].scale];
    }
    //解碼器更新數(shù)據(jù)
    [_progressiveDecoder updateData:_data final:NO];
    if ([self isCancelled]) return;
    //只支持漸進(jìn)式的JPEG圖像和interlanced類型的PNG圖像
    if (_progressiveDecoder.type == YYImageTypeUnknown ||
        _progressiveDecoder.type == YYImageTypeWebP ||
        _progressiveDecoder.type == YYImageTypeOther) {
        _progressiveDecoder = nil;
        _progressiveIgnored = YES;
        return;
    }
    if (progressiveBlur) { // only support progressive JPEG and interlaced PNG
        if (_progressiveDecoder.type != YYImageTypeJPEG &&
            _progressiveDecoder.type != YYImageTypePNG) {
            _progressiveDecoder = nil;
            _progressiveIgnored = YES;
            return;
        }
    }
    if (_progressiveDecoder.frameCount == 0) return;
    //不存在漸進(jìn)顯示的話
    if (!progressiveBlur) {
        YYImageFrame *frame = [_progressiveDecoder frameAtIndex:0 decodeForDisplay:YES];
        if (frame.image) {
            [_lock lock];
            if (![self isCancelled]) {
                _completion(frame.image, _request.URL, YYWebImageFromRemote, YYWebImageStageProgress, nil);
                _lastProgressiveDecodeTimestamp = now;
            }
            [_lock unlock];
        }
        return;
    } else {
        //解碼之后發(fā)現(xiàn)是JPEG格式的
        if (_progressiveDecoder.type == YYImageTypeJPEG) {
            if (!_progressiveDetected) {
               //不是漸進(jìn)式加載
                NSDictionary *dic = [_progressiveDecoder framePropertiesAtIndex:0];
                NSDictionary *jpeg = dic[(id)kCGImagePropertyJFIFDictionary];
                NSNumber *isProg = jpeg[(id)kCGImagePropertyJFIFIsProgressive];
                if (!isProg.boolValue) {
                    _progressiveIgnored = YES;
                    _progressiveDecoder = nil;
                    return;
                }
                _progressiveDetected = YES;
            }
            //縮放長度為 接收到數(shù)據(jù)length - _progressiveScanedLength - 4
            NSInteger scanLength = (NSInteger)_data.length - (NSInteger)_progressiveScanedLength - 4;
            if (scanLength <= 2) return;
            NSRange scanRange = NSMakeRange(_progressiveScanedLength, scanLength);
            NSRange markerRange = [_data rangeOfData:JPEGSOSMarker() options:kNilOptions range:scanRange];
            _progressiveScanedLength = _data.length;
            if (markerRange.location == NSNotFound) return;
            if ([self isCancelled]) return;
            
        } else if (_progressiveDecoder.type == YYImageTypePNG) {
            if (!_progressiveDetected) {
                //從解碼中取值,解碼,賦值
                NSDictionary *dic = [_progressiveDecoder framePropertiesAtIndex:0];
                NSDictionary *png = dic[(id)kCGImagePropertyPNGDictionary];
                NSNumber *isProg = png[(id)kCGImagePropertyPNGInterlaceType];
                if (!isProg.boolValue) {
                    _progressiveIgnored = YES;
                    _progressiveDecoder = nil;
                    return;
                }
                _progressiveDetected = YES;
            }
        }
        
        YYImageFrame *frame = [_progressiveDecoder frameAtIndex:0 decodeForDisplay:YES];
        UIImage *image = frame.image;
        if (!image) return;
        if ([self isCancelled]) return;
        //最后一個像素沒有填充完畢,以為沒有下載成功,返回
        if (!YYCGImageLastPixelFilled(image.CGImage)) return;
        _progressiveDisplayCount++;
        
        CGFloat radius = 32;
        if (_expectedSize > 0) {
            radius *= 1.0 / (3 * _data.length / (CGFloat)_expectedSize + 0.6) - 0.25;
        } else {
            radius /= (_progressiveDisplayCount);
        }
        image = [image yy_imageByBlurRadius:radius tintColor:nil tintMode:0 saturation:1 maskImage:nil];
        
        if (image) {
            [_lock lock];
            if (![self isCancelled]) {
                // block  賦值結(jié)束
                _completion(image, _request.URL, YYWebImageFromRemote, YYWebImageStageProgress, nil);
                _lastProgressiveDecodeTimestamp = now;
            }
            [_lock unlock];
        }
    }
6移剪、此處用的是蘋果已經(jīng)棄用的NSURLConnection究珊,是否后期會替換?

隨著 iOS 10的到來纵苛,在 iOS 9 引入的 ATS 將在來年更加嚴(yán)格剿涮。2017 年起,新提交的 app 將不再被允許進(jìn)行 http 的訪問攻人,所有的 app 內(nèi)的網(wǎng)絡(luò)請求必須加密幔虏,通過 https 完成。所以我想網(wǎng)絡(luò)這塊的NSURLConnection 正式棄用贝椿,已經(jīng)不遠(yuǎn)了想括。自從 AFNetworking 也已經(jīng)更新了 NSURLSession 的情況下,YYWebImage網(wǎng)絡(luò)這塊的更新應(yīng)該也不遠(yuǎn)了烙博,雖說目前使用沒問題瑟蜈,但這個目前持續(xù)的時間不久了。

總的說來渣窜,還是沒怎么吃透铺根,對于源碼中還有很多地方可以推敲學(xué)習(xí)的,鑒于此這塊的學(xué)習(xí)依然會持續(xù)乔宿,方式還是應(yīng)該yi問題持續(xù)中位迂。

備注參考:

https://github.com/ibireme/YYWebImage
http://www.reibang.com/p/5a5bea9180e7
http://www.reibang.com/p/9b71c2d94b89

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市详瑞,隨后出現(xiàn)的幾起案子掂林,更是在濱河造成了極大的恐慌,老刑警劉巖坝橡,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泻帮,死亡現(xiàn)場離奇詭異,居然都是意外死亡计寇,警方通過查閱死者的電腦和手機(jī)锣杂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來番宁,“玉大人元莫,你說我怎么就攤上這事〉海” “怎么了踱蠢?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長播聪。 經(jīng)常有香客問我朽基,道長布隔,這世上最難降的妖魔是什么离陶? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任稼虎,我火速辦了婚禮,結(jié)果婚禮上招刨,老公的妹妹穿的比我還像新娘霎俩。我一直安慰自己,他們只是感情好沉眶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布打却。 她就那樣靜靜地躺著,像睡著了一般谎倔。 火紅的嫁衣襯著肌膚如雪柳击。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天片习,我揣著相機(jī)與錄音捌肴,去河邊找鬼。 笑死藕咏,一個胖子當(dāng)著我的面吹牛状知,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播孽查,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼饥悴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了盲再?” 一聲冷哼從身側(cè)響起西设,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎答朋,沒想到半個月后济榨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绿映,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年擒滑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叉弦。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡丐一,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淹冰,到底是詐尸還是另有隱情库车,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布樱拴,位于F島的核電站柠衍,受9級特大地震影響洋满,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜珍坊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一牺勾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧阵漏,春花似錦驻民、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叹洲,卻和暖如春柠硕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背运提。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工蝗柔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糙捺。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓诫咱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親洪灯。 傳聞我的和親對象是個殘疾皇子坎缭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容

  • __block和__weak修飾符的區(qū)別其實(shí)是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,293評論 0 6
  • 從哪說起呢签钩? 單純講多線程編程真的不知道從哪下嘴掏呼。。 不如我直接引用一個最簡單的問題铅檩,以這個作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,740評論 1 17
  • 史上最全的iOS面試題及答案 iOS面試小貼士———————————————回答好下面的足夠了----------...
    Style_偉閱讀 2,346評論 0 35
  • ———————————————回答好下面的足夠了---------------------------------...
    恒愛DE問候閱讀 1,712評論 0 4
  • 多線程憎夷、特別是NSOperation 和 GCD 的內(nèi)部原理。運(yùn)行時機(jī)制的原理和運(yùn)用場景昧旨。SDWebImage的原...
    LZM輪回閱讀 2,004評論 0 12