多線程03

SDWebImage框架詳解

  • 下載圖片并顯示:
 [cell.imageView sd_setImageWithURL:[NSURL URLWithString:appM.icon] placeholderImage:[UIImage imageNamed:@"Snip20200808_172"]];
  • 下載圖片/顯示圖片/內(nèi)存緩存/磁盤緩存
   -(void)download1
{
 /*
  第一個參數(shù):要下載圖片的URL
  第二個參數(shù):占位圖片
  第三個參數(shù):下載選項
  第四個參數(shù):progress 進度回調(diào)
     receivedSize:已經(jīng)下載的數(shù)據(jù)大小
     expectedSize:圖片的中大小
  第五個參數(shù):completed 完成回調(diào)(成功|失敗)
     cacheType:是否使用了緩存,使用的方式
  */
 [self.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://ww1.sinaimg.cn/crop.0.0.720.720.1024/abe7c97cjw8ermn0v2x7nj20k00k0jrz.jpg"] placeholderImage:[UIImage imageNamed:@"Snip20200808_11"] options:SDWebImageLowPriority | SDWebImageCacheMemoryOnly progress:^(NSInteger receivedSize, NSInteger expectedSize) {
     NSLog(@"%f",(CGFloat)receivedSize/expectedSize);
 } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
 }];
}

  • 下載圖片\內(nèi)存緩存\磁盤緩存
  -(void)download2
{
  [[SDWebImageManager sharedManager]downloadImageWithURL:[NSURL URLWithString:@"http://img.kumi.cn/photo/6b/42/eb/6b42eb5597c4f174.jpg"] options:kNilOptions progress:^(NSInteger receivedSize, NSInteger expectedSize) {
        NSLog(@"%f",(CGFloat)receivedSize/expectedSize);
  } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
      if (error == nil) {
          self.imageView.image = image;
      }
  }];
}

  • 下載圖片(完成后回調(diào)是在子線程中完成處理的)
-(void)download3
{
  [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:@"http://img.kumi.cn/photo/6b/42/eb/6b42eb5597c4f174.jpg"] options:kNilOptions progress:^(NSInteger receivedSize, NSInteger expectedSize) {

  } completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {

      //completed是在子線程中處理的
      dispatch_async(dispatch_get_main_queue(), ^{
          //設(shè)置圖片
          self.imageView.image = image;
      });

  }];
}
  • 顯示gif動畫
   self.imageView.image = [UIImage sd_animatedGIFNamed:@"1234"];
  • 接受到系統(tǒng)內(nèi)存警告時如何處理:
      //(1)取消當前正在進行的所有下載操作
      [[SDWebImageManager sharedManager] cancelAll];
    
      //(2)清除緩存數(shù)據(jù)
      //cleanDisk:刪除過期的文件數(shù)據(jù)采驻,計算當前未過期的已經(jīng)下載的文件數(shù)據(jù)的大小挂洛,如果發(fā)現(xiàn)該數(shù)據(jù)大小大于我們設(shè)置的最大緩存數(shù)據(jù)大小换团,那么程序內(nèi)部會按照按文件數(shù)據(jù)緩存的時間從遠到近刪除协屡,知道小于最大緩存數(shù)據(jù)為止。
      //clearMemory:直接刪除文件茸俭,重新創(chuàng)建新的文件夾
      //[[SDWebImageManager sharedManager].imageCache cleanDisk];
      [[SDWebImageManager sharedManager].imageCache clearMemory];
    
  • SDWebImage內(nèi)部實現(xiàn)細節(jié):
  • 判斷圖片當前類型:之判斷二進制數(shù)據(jù)的第一個字節(jié)
  • 默認緩存周期:一周
  • 緩存策略:默認進行內(nèi)存和磁盤緩存吊履,下載時首先檢查內(nèi)存緩存,其次是磁盤緩存
  • 緩存實現(xiàn)方式:采用了蘋果推出的用來處理緩存的NSCache
  • 對內(nèi)存警告的處理:框架內(nèi)部監(jiān)聽系統(tǒng)內(nèi)存警告的通知调鬓,當發(fā)生時艇炎,自動移除緩存中的所有對象
  • 下載隊列中對多個圖片任務(wù)采取的措施:方式有FIFO以及LIFO兩種方式,默認是FIFO
  • 框架內(nèi)允許的最大并發(fā)數(shù)6
  • 磁盤緩存圖片的命名:對圖片url進行md5散列加密(【echo -n "url" |MD5】)

NSCache詳解

  • NSCache簡單說明:
  • NSCache是蘋果用來管理內(nèi)存的類腾窝,類似于MutibleArray缀踪,在SDWebImage和AFN等框架中廣泛用來管理緩存
  • NSCache在內(nèi)存過低時會自動釋放對象(我們要手動釋放對象)
  • NSCache是線程安全的居砖,在使用過程中不需要加鎖
  • NSCache的Key只是對對象進行Strong引用,不是拷貝驴娃,在清理的時候計算的是實際大小而不是引用的大凶嗪颉(不明白)
  • NSCache屬性以及方法介紹
  • 1)屬性介紹
    • name:名稱
    • delegete:設(shè)置代理
    • totalCostLimit:緩存空間的最大總成本,超出上限會自動回收對象唇敞。默認值為0蔗草,表示沒有限制
    • countLimit:能夠緩存的對象的最大數(shù)量。默認值為0疆柔,表示沒有限制
    • evictsObjectsWithDiscardedContent:標識緩存是否回收廢棄的內(nèi)容
  • 2)方法介紹
    objc - (void)setObject:(ObjectType)obj forKey:(KeyType)key;//在緩存中設(shè)置指定鍵名對應(yīng)的值咒精,0成本 - (void)setObject:(ObjectType)obj forKey:(KeyType)keycost:(NSUInteger)g; //在緩存中設(shè)置指定鍵名對應(yīng)的值,并且指定該鍵值對的成本旷档,用于計算記錄在緩存中的所有對象的總成本 //當出現(xiàn)內(nèi)存警告或者超出緩存總成本上限的時候模叙,緩存會開啟一個回收過程,刪除部分元素 - (void)removeObjectForKey:(KeyType)key;//刪除緩存中指定鍵名的對象 - (void)removeAllObjects;//刪除緩存中所有的對象

位移的簡單說明

  • 常見的幾種枚舉形式:
//枚舉一
  typedef enum{

      XMGDemoTypeTop,
      XMGDemoTypeBottom,

  }XMGDemoType;

  //枚舉二
  typedef NS_ENUM(NSInteger,XMGType)
  {
      XMGTypeTop,
      XMGTypeBottom,
  };

  //枚舉三:位移枚舉
  typedef NS_OPTIONS(NSInteger, XMGActionType)
  {
      XMGActionTypeTop = 1<<0,
      XMGActionTypeBottom = 1<<1,
      XMGActionTypeLeft = 1<<2,
      XMGActionTypeRight = 1 <<3,
  };
  • 位移枚舉相關(guān)說明
    • 特點:通過使用位移枚舉可以實現(xiàn)一個參數(shù)實現(xiàn)傳遞多個操作
    • 原理:按位與只要有0則為0彬犯,按位或只要有1則為1
    • 技巧:如果位移枚舉的第一個選項為0向楼,那么在傳遞參數(shù)的時候默認可以傳0,傳0性能最優(yōu)谐区,不做額外的操作

RunLoop介紹:

  • 基礎(chǔ)知識:

  • 基本作用:

  • 保證程序不退出(死循環(huán))

  • 處理各種事件(觸摸事件、定時器事件逻卖、selector事件等)

  • 節(jié)省cpu資源宋列,提高程序性能,該運行時運行评也,該休息時休息

  • RunLoop對象

    • 在iOS開發(fā)中有兩套api來訪問Runloop
      foundation框架【NSRunloop】
      core foundation框架【CFRunloopRef】
    • NSRunLoop和CFRunLoopRef都代表著RunLoop對象,它們是等價的炼杖,可以互相轉(zhuǎn)換
    • NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu)盗迟,需要多研究CFRunLoopRef層面的API(Core Foundation層面)
  • RunLoop與線程關(guān)系

  • RunLoop與子線程關(guān)系:一個RunLoop對應(yīng)唯一的一個線程

  • RunLoop生命周期:在第一次獲取時創(chuàng)建坤邪,在相對應(yīng)線程死亡的時候銷毀

  • RunLoop的創(chuàng)建:主線程已經(jīng)創(chuàng)建好,子線程需要手動創(chuàng)建

  • 如何獲得RunLoop對象:

    • 獲得當前RunLoop對象:
          //01 NSRunloop
            NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
          //02 CFRunLoopRef
            CFRunLoopRef runloop2 =   CFRunLoopGetCurrent();
      
    • 拿到當前對應(yīng)程序的主線程
      //01 NSRunloop
        NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];
      //02 CFRunLoopRef
        CFRunLoopRef runloop2 =   CFRunLoopGetMain();
    
    • 注意點:開一個子線程創(chuàng)建runloop,不是通過alloc init方法創(chuàng)建罚缕,而是直接通過調(diào)用currentRunLoop方法來創(chuàng)建艇纺,它本身是一個懶加載的。
    • 在子線程中邮弹,如果不主動獲取Runloop的話黔衡,那么子線程內(nèi)部是不會創(chuàng)建Runloop的‰缦纾可以下載CFRunloopRef的源碼盟劫,搜索_CFRunloopGet0,查看代碼。
    • Runloop對象是利用字典來進行存儲与纽,而且key是對應(yīng)的線程Value為該線程對應(yīng)的Runloop侣签。
    • RunLoop相關(guān)類
      • RunLoop運行原理圖
2.png

- RunLoop與相關(guān)類之間的關(guān)系圖

1.png
5)CFRunloopTimerRef
        (1)NSTimer相關(guān)代碼
            /*
                說明:
                (1)runloop一啟動就會選中一種模式塘装,當選中了一種模式之后其它的模式就都不鳥。一個mode里面可以添加多個NSTimer,也就是說以后當創(chuàng)建NSTimer的時候影所,可以指定它是在什么模式下運行的氢哮。
                (2)它是基于時間的觸發(fā)器,說直白點那就是時間到了我就觸發(fā)一個事件型檀,觸發(fā)一個操作冗尤。基本上說的就是NSTimer
                (3)相關(guān)代碼
            */
            - (void)timer2
            {
                //NSTimer 調(diào)用了scheduledTimer方法胀溺,那么會自動添加到當前的runloop里面去裂七,而且runloop的運行模式kCFRunLoopDefaultMode

                NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

                //更改模式
                [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

            }

            - (void)timer1
            {
                //    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

                NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

                //定時器添加到UITrackingRunLoopMode模式,一旦runloop切換模式仓坞,那么定時器就不工作
                //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

                //定時器添加到NSDefaultRunLoopMode模式背零,一旦runloop切換模式,那么定時器就不工作
                //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

                //占位模式:common modes標記
                //被標記為common modes的模式 kCFRunLoopDefaultMode  UITrackingRunLoopMode
                [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

                //    NSLog(@"%@",[NSRunLoop currentRunLoop]);
            }

            - (void)run
            {
                NSLog(@"---run---%@",[NSRunLoop currentRunLoop].currentMode);
            }

            - (IBAction)btnClick {

                NSLog(@"---btnClick---");
            }

        (2)GCD中的定時器
            //0.創(chuàng)建一個隊列
            dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

            //1.創(chuàng)建一個GCD的定時器
            /*
             第一個參數(shù):說明這是一個定時器
             第四個參數(shù):GCD的回調(diào)任務(wù)添加到那個隊列中執(zhí)行无埃,如果是主隊列則在主線程執(zhí)行
             */
            dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

            //2.設(shè)置定時器的開始時間徙瓶,間隔時間以及精準度

            //設(shè)置開始時間,三秒鐘之后調(diào)用
            dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,3.0 *NSEC_PER_SEC);
            //設(shè)置定時器工作的間隔時間
            uint64_t intevel = 1.0 * NSEC_PER_SEC;

            /*
             第一個參數(shù):要給哪個定時器設(shè)置
             第二個參數(shù):定時器的開始時間DISPATCH_TIME_NOW表示從當前開始
             第三個參數(shù):定時器調(diào)用方法的間隔時間
             第四個參數(shù):定時器的精準度嫉称,如果傳0則表示采用最精準的方式計算侦镇,如果傳大于0的數(shù)值,則表示該定時切換i可以接收該值范圍內(nèi)的誤差织阅,通常傳0
             該參數(shù)的意義:可以適當?shù)奶岣叱绦虻男阅?             注意點:GCD定時器中的時間以納秒為單位(面試)
             */

            dispatch_source_set_timer(timer, start, intevel, 0 * NSEC_PER_SEC);

            //3.設(shè)置定時器開啟后回調(diào)的方法
            /*
             第一個參數(shù):要給哪個定時器設(shè)置
             第二個參數(shù):回調(diào)block
             */
            dispatch_source_set_event_handler(timer, ^{
                NSLog(@"------%@",[NSThread currentThread]);
            });

            //4.執(zhí)行定時器
            dispatch_resume(timer);

            //注意:dispatch_source_t本質(zhì)上是OC類壳繁,在這里是個局部變量,需要強引用
            self.timer = timer;

            GCD定時器補充
            /*
             DISPATCH_SOURCE_TYPE_TIMER         定時響應(yīng)(定時器事件)
             DISPATCH_SOURCE_TYPE_SIGNAL        接收到UNIX信號時響應(yīng)

             DISPATCH_SOURCE_TYPE_READ          IO操作荔棉,如對文件的操作闹炉、socket操作的讀響應(yīng)
             DISPATCH_SOURCE_TYPE_WRITE         IO操作,如對文件的操作润樱、socket操作的寫響應(yīng)
             DISPATCH_SOURCE_TYPE_VNODE         文件狀態(tài)監(jiān)聽渣触,文件被刪除、移動壹若、重命名
             DISPATCH_SOURCE_TYPE_PROC          進程監(jiān)聽,如進程的退出嗅钻、創(chuàng)建一個或更多的子線程、進程收到UNIX信號

             下面兩個都屬于Mach相關(guān)事件響應(yīng)
                DISPATCH_SOURCE_TYPE_MACH_SEND
                DISPATCH_SOURCE_TYPE_MACH_RECV
             下面兩個都屬于自定義的事件舌稀,并且也是有自己來觸發(fā)
                DISPATCH_SOURCE_TYPE_DATA_ADD
                DISPATCH_SOURCE_TYPE_DATA_OR
             */

6)CFRunloopSourceRef
        (1)是事件源也就是輸入源啊犬,有兩種分類模式;
              a.一種是按照蘋果官方文檔進行劃分的
              b.另一種是基于函數(shù)的調(diào)用棧來進行劃分的(source0和source1)壁查。
        (2)具體的分類情況
            a.以前的分法
                Port-Based Sources
                Custom Input Sources
                Cocoa Perform Selector Sources
            b.現(xiàn)在的分法
                Source0:非基于Port的
                Source1:基于Port的
        (3)可以通過打斷點的方式查看一個方法的函數(shù)調(diào)用棧

    7)CFRunLoopObserverRef
        (1)CFRunLoopObserverRef是觀察者觉至,能夠監(jiān)聽RunLoop的狀態(tài)改變
        (2)如何監(jiān)聽
             //創(chuàng)建一個runloop監(jiān)聽者
                CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

                    NSLog(@"監(jiān)聽runloop狀態(tài)改變---%zd",activity);
                });

                //為runloop添加一個監(jiān)聽者
                CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

                CFRelease(observer);
        (3)監(jiān)聽的狀態(tài)
            typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
                kCFRunLoopEntry = (1UL << 0),   //即將進入Runloop
                kCFRunLoopBeforeTimers = (1UL << 1),    //即將處理NSTimer
                kCFRunLoopBeforeSources = (1UL << 2),   //即將處理Sources
                kCFRunLoopBeforeWaiting = (1UL << 5),   //即將進入休眠
                kCFRunLoopAfterWaiting = (1UL << 6),    //剛從休眠中喚醒
                kCFRunLoopExit = (1UL << 7),            //即將退出runloop
                kCFRunLoopAllActivities = 0x0FFFFFFFU   //所有狀態(tài)改變
            };

3)Runloop運行邏輯


3.png
4.png

4.Runloop應(yīng)用
1)NSTimer
2)ImageView顯示:控制方法在特定的模式下可用
3)PerformSelector
4)常駐線程:在子線程中開啟一個runloop
5)自動釋放池
第一次創(chuàng)建:進入runloop的時候
最后一次釋放:runloop退出的時候
其它創(chuàng)建和釋放:當runloop即將休眠的時候會把之前的自動釋放池釋放,然后重新創(chuàng)建一個新的釋放池

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睡腿,一起剝皮案震驚了整個濱河市语御,隨后出現(xiàn)的幾起案子峻贮,更是在濱河造成了極大的恐慌,老刑警劉巖应闯,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纤控,死亡現(xiàn)場離奇詭異,居然都是意外死亡碉纺,警方通過查閱死者的電腦和手機船万,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骨田,“玉大人耿导,你說我怎么就攤上這事√停” “怎么了舱呻?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悠汽。 經(jīng)常有香客問我箱吕,道長,這世上最難降的妖魔是什么柿冲? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任茬高,我火速辦了婚禮,結(jié)果婚禮上姻采,老公的妹妹穿的比我還像新娘雅采。我一直安慰自己,他們只是感情好慨亲,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宝鼓,像睡著了一般刑棵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愚铡,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天蛉签,我揣著相機與錄音,去河邊找鬼沥寥。 笑死碍舍,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的邑雅。 我是一名探鬼主播片橡,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淮野!你這毒婦竟也來了捧书?” 一聲冷哼從身側(cè)響起吹泡,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎经瓷,沒想到半個月后爆哑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡舆吮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年揭朝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片色冀。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡潭袱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呐伞,到底是詐尸還是另有隱情敌卓,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布伶氢,位于F島的核電站趟径,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏癣防。R本人自食惡果不足惜蜗巧,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蕾盯。 院中可真熱鬧幕屹,春花似錦、人聲如沸级遭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挫鸽。三九已至说敏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丢郊,已是汗流浹背盔沫。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枫匾,地道東北人架诞。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像干茉,于是被迫代替她去往敵國和親谴忧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

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