多線程03

SDWebImage框架詳解

  • 下載圖片并顯示:
 [cell.imageView sd_setImageWithURL:[NSURL URLWithString:appM.icon] placeholderImage:[UIImage imageNamed:@"Snip20200808_172"]];
  • 下載圖片/顯示圖片/內存緩存/磁盤緩存
   -(void)download1
{
 /*
  第一個參數:要下載圖片的URL
  第二個參數:占位圖片
  第三個參數:下載選項
  第四個參數:progress 進度回調
     receivedSize:已經下載的數據大小
     expectedSize:圖片的中大小
  第五個參數:completed 完成回調(成功|失敗)
     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) {
 }];
}

  • 下載圖片\內存緩存\磁盤緩存
  -(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;
      }
  }];
}

  • 下載圖片(完成后回調是在子線程中完成處理的)
-(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(), ^{
          //設置圖片
          self.imageView.image = image;
      });

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

NSCache詳解

  • NSCache簡單說明:
  • NSCache是蘋果用來管理內存的類,類似于MutibleArray,在SDWebImage和AFN等框架中廣泛用來管理緩存
  • NSCache在內存過低時會自動釋放對象(我們要手動釋放對象)
  • NSCache是線程安全的,在使用過程中不需要加鎖
  • NSCache的Key只是對對象進行Strong引用,不是拷貝,在清理的時候計算的是實際大小而不是引用的大小(不明白)
  • NSCache屬性以及方法介紹
  • 1)屬性介紹
    • name:名稱
    • delegete:設置代理
    • totalCostLimit:緩存空間的最大總成本,超出上限會自動回收對象。默認值為0,表示沒有限制
    • countLimit:能夠緩存的對象的最大數量。默認值為0,表示沒有限制
    • evictsObjectsWithDiscardedContent:標識緩存是否回收廢棄的內容
  • 2)方法介紹
    objc - (void)setObject:(ObjectType)obj forKey:(KeyType)key;//在緩存中設置指定鍵名對應的值,0成本 - (void)setObject:(ObjectType)obj forKey:(KeyType)keycost:(NSUInteger)g; //在緩存中設置指定鍵名對應的值,并且指定該鍵值對的成本,用于計算記錄在緩存中的所有對象的總成本 //當出現內存警告或者超出緩存總成本上限的時候,緩存會開啟一個回收過程,刪除部分元素 - (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,
  };
  • 位移枚舉相關說明
    • 特點:通過使用位移枚舉可以實現一個參數實現傳遞多個操作
    • 原理:按位與只要有0則為0,按位或只要有1則為1
    • 技巧:如果位移枚舉的第一個選項為0,那么在傳遞參數的時候默認可以傳0,傳0性能最優,不做額外的操作

RunLoop介紹:

  • 基礎知識:

  • 基本作用:

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

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

  • 節省cpu資源,提高程序性能,該運行時運行,該休息時休息

  • RunLoop對象

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

  • RunLoop與子線程關系:一個RunLoop對應唯一的一個線程

  • RunLoop生命周期:在第一次獲取時創建,在相對應線程死亡的時候銷毀

  • RunLoop的創建:主線程已經創建好,子線程需要手動創建

  • 如何獲得RunLoop對象:

    • 獲得當前RunLoop對象:
          //01 NSRunloop
            NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
          //02 CFRunLoopRef
            CFRunLoopRef runloop2 =   CFRunLoopGetCurrent();
      
    • 拿到當前對應程序的主線程
      //01 NSRunloop
        NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];
      //02 CFRunLoopRef
        CFRunLoopRef runloop2 =   CFRunLoopGetMain();
    
    • 注意點:開一個子線程創建runloop,不是通過alloc init方法創建,而是直接通過調用currentRunLoop方法來創建,它本身是一個懶加載的。
    • 在子線程中,如果不主動獲取Runloop的話,那么子線程內部是不會創建Runloop的。可以下載CFRunloopRef的源碼,搜索_CFRunloopGet0,查看代碼。
    • Runloop對象是利用字典來進行存儲,而且key是對應的線程Value為該線程對應的Runloop。
    • RunLoop相關類
      • RunLoop運行原理圖
2.png

- RunLoop與相關類之間的關系圖

1.png
5)CFRunloopTimerRef
        (1)NSTimer相關代碼
            /*
                說明:
                (1)runloop一啟動就會選中一種模式,當選中了一種模式之后其它的模式就都不鳥。一個mode里面可以添加多個NSTimer,也就是說以后當創建NSTimer的時候,可以指定它是在什么模式下運行的。
                (2)它是基于時間的觸發器,說直白點那就是時間到了我就觸發一個事件,觸發一個操作。基本上說的就是NSTimer
                (3)相關代碼
            */
            - (void)timer2
            {
                //NSTimer 調用了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.創建一個隊列
            dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

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

            //2.設置定時器的開始時間,間隔時間以及精準度

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

            /*
             第一個參數:要給哪個定時器設置
             第二個參數:定時器的開始時間DISPATCH_TIME_NOW表示從當前開始
             第三個參數:定時器調用方法的間隔時間
             第四個參數:定時器的精準度,如果傳0則表示采用最精準的方式計算,如果傳大于0的數值,則表示該定時切換i可以接收該值范圍內的誤差,通常傳0
             該參數的意義:可以適當的提高程序的性能
             注意點:GCD定時器中的時間以納秒為單位(面試)
             */

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

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

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

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

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

             DISPATCH_SOURCE_TYPE_READ          IO操作,如對文件的操作、socket操作的讀響應
             DISPATCH_SOURCE_TYPE_WRITE         IO操作,如對文件的操作、socket操作的寫響應
             DISPATCH_SOURCE_TYPE_VNODE         文件狀態監聽,文件被刪除、移動、重命名
             DISPATCH_SOURCE_TYPE_PROC          進程監聽,如進程的退出、創建一個或更多的子線程、進程收到UNIX信號

             下面兩個都屬于Mach相關事件響應
                DISPATCH_SOURCE_TYPE_MACH_SEND
                DISPATCH_SOURCE_TYPE_MACH_RECV
             下面兩個都屬于自定義的事件,并且也是有自己來觸發
                DISPATCH_SOURCE_TYPE_DATA_ADD
                DISPATCH_SOURCE_TYPE_DATA_OR
             */

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

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

                    NSLog(@"監聽runloop狀態改變---%zd",activity);
                });

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

                CFRelease(observer);
        (3)監聽的狀態
            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   //所有狀態改變
            };

3)Runloop運行邏輯


3.png
4.png

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容