NSRunLoop簡(jiǎn)介

NSRunLoop簡(jiǎn)介

一. 什么是RunLoop

  1. RunLoop

    • 從字面上了解, RunLoop即是運(yùn)行循環(huán), 就像是在一個(gè)圓形循環(huán)中去運(yùn)作
    • RunLoop的基本作用
      • 他是App持續(xù)運(yùn)行的保證, 如果RunLoop不存在了, 程序也就終止運(yùn)行了
      • RunLoop會(huì)在循環(huán)中處理App的各種事件, 如觸摸事件, 定時(shí)器事件, Selector事件
      • RunLoop最大的優(yōu)勢(shì)就是能節(jié)省CPU的資源, 提高程序的性能, 他會(huì)在需要執(zhí)行任務(wù)的時(shí)候被喚醒, 當(dāng)沒有任務(wù)執(zhí)行的時(shí)候進(jìn)入休眠狀態(tài)
  2. Main函數(shù)中的RunLoop

    • 首先, 重溫一遍App的啟動(dòng)原理

    • 當(dāng)Main函數(shù)執(zhí)行到UIApplicationMain時(shí), 就開啟了RunLoop運(yùn)行循環(huán)

    • 在運(yùn)行循環(huán)開啟時(shí), 就會(huì)保證程序的持續(xù)運(yùn)行并且處理App的各種事件, 不會(huì)退出

    • Main函數(shù)中的RunLoop, 被稱為主運(yùn)行循環(huán), 而主運(yùn)行循環(huán)在整個(gè)App的聲明周期中都不會(huì)被銷毀, 他是程序運(yùn)行的保證

        // 程序在啟動(dòng)時(shí)近尚,第一步就會(huì)執(zhí)行main函數(shù),在main函數(shù)中會(huì)執(zhí)行以下操作:
        int main(int argc, char * argv[]) {
            @autoreleasepool {
                /*
                 nil:UIApplication類名或者子類名稱吊骤,如果為nil,就等于@"UIApplication"
                 NSStringFromClass([AppDelegate class]:UIApplication代理的名稱
                 */
                return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
            }
        }
        
        程序啟動(dòng)的完整流程
             1. 執(zhí)行main函數(shù)
             2. 執(zhí)行UIApplicationMain函數(shù)
                  1> 指定UIApplication對(duì)象
                  2> 指定UIApplication的代理
             3. 創(chuàng)建UIApplication對(duì)象跷究,并且指定他的代理
             4. **創(chuàng)建一個(gè)事件循環(huán):主循環(huán)(RunLoop)蹬挺,并且是一個(gè)死循環(huán),保證程序的持續(xù)運(yùn)行**
             5. 加載配置了所有應(yīng)用程序信息的info.plist文件
                  1> 判斷 Main storyboard file base name中有沒有指定Main沐序,即需要加載的StoryBoard文件
                  2> 如果指定了邑茄,就加載Main.storyboard
                  3> 如果沒有指定的話姨蝴,就會(huì)黑屏
             6. 應(yīng)用程序啟動(dòng)完畢
      

二. NSRunLoop和CFRunLoopRef

  1. 簡(jiǎn)單介紹

    • CFRunLoopRef是在CoreFoundation框架中的, 它的內(nèi)部API以及實(shí)現(xiàn), 都是純C語言編寫, 這些API都是現(xiàn)成安全的
    • NSRunLoop是基于CFRunLoopRef的封裝, 他提供了面向?qū)ο蟮腁PI, 但是這些API不是線程安全的
    • 目前CFRunLoopRef已經(jīng)開源了, 大家可以在官方文檔中查看: 友情提示: 需要很好的C語言功底
  2. NSRunloop和CFRunLoopRef都是RunLoop對(duì)象, 他們的區(qū)別是

    • 這兩個(gè)對(duì)象的地址不同, 因?yàn)樗麄兊膶?duì)象來自于完全不同的類
    • CFRunLoop可以調(diào)用getCfRunLoop方法, 將NSRunLoop轉(zhuǎn)化為CFRunLoop
    • 線程: RunLoop在主線程中, 保持持續(xù)循環(huán)狀態(tài), 當(dāng)所有的事件處理結(jié)束, 就會(huì)進(jìn)入休眠狀態(tài)
    • 當(dāng)外界傳入各種時(shí)間時(shí): Prot接口事件時(shí), RunLoop就會(huì)被喚醒, 處理相關(guān)的事件, 當(dāng)事件處理完畢時(shí), 會(huì)再次進(jìn)入休眠狀態(tài)
      • UI交互事件
      • PerformSelector: onThread: 讓線程執(zhí)行任務(wù)
      • Timer: 定時(shí)器事件
  3. RunLoop運(yùn)行圖解

screenshot.png
  1. 簡(jiǎn)單的應(yīng)用

     - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
         
         // 1. 獲取當(dāng)前線程對(duì)應(yīng)的RunLoop對(duì)象
         NSRunLoop *curRunLoop = [NSRunLoop currentRunLoop];
         NSLog(@"%p", curRunLoop);
         // 2. 獲取主線程對(duì)應(yīng)的RunLoop對(duì)象
         NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
         NSLog(@"%p", mainRunLoop);
         
         /* Core Foundation */
         // 1. 獲得當(dāng)前線程的RunLoop對(duì)象
         CFRunLoopRef runloop = CFRunLoopGetCurrent();
         NSLog(@"%p", runloop);
         // 2. 獲得主線程的RunLoop對(duì)象
         CFRunLoopRef cfMainRunLoop = CFRunLoopGetMain();
         NSLog(@"%p", cfMainRunLoop);
    
         // 從這里可以看出這兩種運(yùn)行循環(huán)是完全不同的對(duì)象
         
         // NSRunLoop --> CFRunLoopRef
         NSLog(@"%p---%p", cfMainRunLoop, mainRunLoop.getCFRunLoop);
         
         // 開啟子線程,執(zhí)行task方法
         [NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];
     }
     
     - (void)task {
         
         /* 子線程和RunLoop
          1. 每一個(gè)子線程肺缕,都對(duì)應(yīng)一個(gè)自己的RunLoop
          2. 主線程的RunLoop在程序運(yùn)行的時(shí)候就已經(jīng)創(chuàng)建了左医,而子線程的RunLoop則需要手動(dòng)開啟
          3. [NSRunLoop currentRunLoop],此方法會(huì)開啟一個(gè)新的RunLoop
          4. RunLoop需要執(zhí)行run方法同木,來開啟浮梢,但如果RunLoop中沒有任何任務(wù),就會(huì)關(guān)閉
          */
         
         // 1. 當(dāng)前RunLoop
         NSLog(@"%p--%p", [NSRunLoop currentRunLoop], [NSRunLoop mainRunLoop]);
         
         // 2. 開啟一個(gè)新的RunLoop
         [[NSRunLoop currentRunLoop] run];
         
         NSLog(@"tast---%@", [NSThread currentThread]);
     }
    

三. RunLoop與線程

  1. 每一條線程, 都有一個(gè)與之相對(duì)應(yīng)的RunLoop對(duì)象, 負(fù)責(zé)處理線程中的任務(wù)
  2. 線程的創(chuàng)建
    • 主線程: RunLoop是在程序已經(jīng)啟動(dòng)的時(shí)候就創(chuàng)建好了, 當(dāng)程序關(guān)閉的時(shí)候主線程才被銷毀
    • 子線程: 子線程需要手動(dòng)創(chuàng)建RunLoop, 并且手動(dòng)開啟, 當(dāng)沒有任務(wù)執(zhí)行時(shí), 該線程會(huì)被關(guān)閉, RunLoop被銷毀
  3. 子線程和RunLoop
    • 子線程會(huì)單獨(dú)開啟RunLoop去執(zhí)行任務(wù)

    • 子線程和RunLoop是一一對(duì)應(yīng)的關(guān)系, 每個(gè)子線程都有自己的RunLoop(但需要主動(dòng)創(chuàng)建)

    • 創(chuàng)建子線程的RunLoop: [NSRunloop currentRunLoop]

      • 通過對(duì)CFRunLoop原碼的分析可以判斷出, 這個(gè)方法是懶加載獲取RunLoop對(duì)象的, 當(dāng)?shù)谝淮握{(diào)用這個(gè)方法時(shí), 他就會(huì)在對(duì)應(yīng)的線程中創(chuàng)建一個(gè)RunLoop, 并且保存到一個(gè)字典中便于隨時(shí)取出
      • 也就是說, 如果不主動(dòng)去獲取RunLoop, 那么默認(rèn)是不會(huì)給子線程創(chuàng)建一個(gè)RunLoop的
    • 子線程的RunLoop需要手動(dòng)開啟: [[NSRunLoop currentRunLoop] run]

    • 如果RunLoop內(nèi)部沒有任何任務(wù)需要去處理時(shí), 就會(huì)被關(guān)閉

        // 全局的Dictionary彤路,key 是 pthread_t黔寇, value 是 CFRunLoopRef
        static CFMutableDictionaryRef loopsDic;
        // 訪問 loopsDic 時(shí)的鎖
        static CFSpinLock_t loopsLock;
          
        // 獲取一個(gè) pthread 對(duì)應(yīng)的 RunLoop
        CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
            OSSpinLockLock(&loopsLock);
             
            if (!loopsDic) {
                // 第一次進(jìn)入時(shí),初始化全局Dic斩萌,并先為主線程創(chuàng)建一個(gè) RunLoop缝裤。
                loopsDic = CFDictionaryCreateMutable();
                CFRunLoopRef mainLoop = _CFRunLoopCreate();
                CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
            }
             
            // 直接從 Dictionary 里獲取。
            CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
             
            if (!loop) {
                // 取不到時(shí)颊郎,創(chuàng)建一個(gè)
                loop = _CFRunLoopCreate();
                CFDictionarySetValue(loopsDic, thread, loop);
                // 注冊(cè)一個(gè)回調(diào)憋飞,當(dāng)線程銷毀時(shí),順便也銷毀其對(duì)應(yīng)的 RunLoop
                _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
            }
             
            OSSpinLockUnLock(&loopsLock);
            return loop;
        }
          
        CFRunLoopRef CFRunLoopGetMain() {
            return _CFRunLoopGet(pthread_main_thread_np());
        }
          
        CFRunLoopRef CFRunLoopGetCurrent() {
            return _CFRunLoopGet(pthread_self());
        }
      

四. RunLoop的相關(guān)類

  1. RunLoop的運(yùn)行模式圖
    • 一個(gè)RunLoop包含多個(gè)Mode, 而每個(gè)Mode又包含若干個(gè)Source/Timer/Observer
CD7555A3-6FA1-4D7B-8290-A9B170C4ECFA.png
  1. 相關(guān)類

    1. RunLoop: RunLoop對(duì)象本身
    2. RunLoopMode: 即RunLoop的運(yùn)行模式
      • 一個(gè)RunLoop至少要制定一個(gè)運(yùn)行模式, 當(dāng)運(yùn)行模式指定之后, 至少有一個(gè)Source或者Timer任務(wù)在執(zhí)行
      • RunLoop啟動(dòng)之后, 只能指定一個(gè)運(yùn)行模式, 可以使用currentMode來獲取
      • 如果要切換RunLoop的運(yùn)行模式, 就要先退出當(dāng)前的RunLoop, 重新指定Mode再次進(jìn)入運(yùn)行
    3. 系統(tǒng)默認(rèn)注冊(cè)的5個(gè)Mode
      • kCFRunLoopDefaultMode: App的默認(rèn)Mode, 通常主線程是在這個(gè)Mode下運(yùn)行的
      • UITrackingRunLoopMode: 界面跟蹤Mode, 用于ScrollView/TableView等追蹤觸摸滑動(dòng), 保證界面滑動(dòng)的時(shí)候不受其他Mode影響
      • UIInitializationRunLoopMode: 當(dāng)App啟動(dòng)時(shí), 第一個(gè)進(jìn)入的Mode, 啟動(dòng)完成之后就不會(huì)再使用這個(gè)Mode
      • GSEventReceiveRunLoopMode: 接收系統(tǒng)事件的內(nèi)部Mode, 通常由系統(tǒng)自動(dòng)管理
      • kCFRunLoopCommonMode: 一個(gè)類似于占位的Mode, 并不是一個(gè)真正的Mode
  2. Mode中的類

    1. CFRunLoopSourceRef: 事件源, 事件, 輸入等都屬于事件源, 他有兩個(gè)分類
      • Source0, 非基于Port, 用戶觸發(fā)的事件, 例如點(diǎn)擊事件等
      • Source1, 基于Port的事件, 他用于系統(tǒng)內(nèi)部與線程之間交互
    2. CFRunLoopTimerRef: 定時(shí)器事件
      • NSTimer:
        • 如果將NSTimer添加到子線程中, 需要先創(chuàng)建一個(gè)RunLoop, 然后再啟動(dòng)RunLoop
        • NSTimer受到RunLoop的印象, 一般會(huì)有一些輕微的誤差, 所以對(duì)于精密計(jì)時(shí), GCD定時(shí)器較為精準(zhǔn)
      • GCD定時(shí)器: 精確到納秒, 較為精準(zhǔn)
        • GCD定時(shí)器的創(chuàng)建步驟: 創(chuàng)建定時(shí)器 -> 設(shè)置定時(shí)器 -> 設(shè)置定時(shí)器的回調(diào)方法 -> 恢復(fù)定時(shí)器

        • GCD定時(shí)器一定要添加一個(gè)強(qiáng)引用, 否則會(huì)被立即釋放

            - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
                
                // 1. 創(chuàng)建GCD定時(shí)器
                /*
                 DISPATCH_SOURCE_TYPE_TIMER 定時(shí)器
                 uintptr_t handle  描述信息
                 unsigned long mask  傳入0
                 dispatch_queue_t queue  定時(shí)器運(yùn)行的隊(duì)列姆吭,決定定時(shí)器在哪個(gè)線程中運(yùn)行
                 */
                dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
                
                // 2. 設(shè)置定時(shí)器
                /*
                 dispatch_source_t source,  定時(shí)器的對(duì)象
                 dispatch_time_t start,     定時(shí)器什么時(shí)候開始
                 uint64_t interval,         定時(shí)器多長(zhǎng)時(shí)間執(zhí)行一次
                 uint64_t leeway            精準(zhǔn)度榛做,0為絕對(duì)精準(zhǔn)
                 */
                dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
                
                // 3. 設(shè)置定時(shí)器的任務(wù)
                dispatch_source_set_event_handler(timer, ^{
                    NSLog(@"---GCD---%@", [NSThread currentThread]);
                });
                
                // 4. 恢復(fù)定時(shí)器
                dispatch_resume(timer);
                
                // 5. 強(qiáng)引用定時(shí)器,否則創(chuàng)建出來就會(huì)被釋放
                self.timer = timer;
            }
          
    3. CFRunLoopObserverRef: 觀察者
      • 觀察者可以觀察到RunLoop不同的運(yùn)行狀態(tài)

      • 通過判斷RunLoop的運(yùn)行狀態(tài), 可以執(zhí)行一些操作

          // 1. 創(chuàng)建監(jiān)聽者
          /*
           CFAllocatorRef allocator 分配存儲(chǔ)空間
           CFOptionFlags activities 要監(jiān)聽哪個(gè)狀態(tài)内狸,kCFRunLoopAllActivities監(jiān)聽所有狀態(tài)
           Boolean repeats 是否持續(xù)監(jiān)聽RunLoop的狀態(tài)
           CFIndex order 優(yōu)先級(jí)检眯,默認(rèn)為0
           Block activity RunLoop當(dāng)前的狀態(tài)
           */
          CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        
              /*
               kCFRunLoopEntry = (1UL << 0),          進(jìn)入工作
               kCFRunLoopBeforeTimers = (1UL << 1),   即將處理Timers事件
               kCFRunLoopBeforeSources = (1UL << 2),  即將處理Source事件
               kCFRunLoopBeforeWaiting = (1UL << 5),  即將休眠
               kCFRunLoopAfterWaiting = (1UL << 6),   被喚醒
               kCFRunLoopExit = (1UL << 7),           退出RunLoop
               kCFRunLoopAllActivities = 0x0FFFFFFFU  監(jiān)聽所有事件
               */
        
              // 當(dāng)activity處于什么狀態(tài)的時(shí)候,調(diào)用一次
              switch (activity) {
                  case kCFRunLoopEntry:
                      NSLog(@"進(jìn)入");
                      break;
                  case kCFRunLoopBeforeTimers:
                      NSLog(@"即將處理Timer事件");
                      break;
                  case kCFRunLoopBeforeSources:
                      NSLog(@"即將處理Source事件");
                      break;
                  case kCFRunLoopBeforeWaiting:
                      NSLog(@"即將休眠");
                      break;
                  case kCFRunLoopAfterWaiting:
                      NSLog(@"被喚醒");
                      break;
                  case kCFRunLoopExit:
                      NSLog(@"退出RunLoop");
                      break;
                  default:
                      break;
              }
          });
        
          // 2. 給對(duì)應(yīng)的RunLoop添加一個(gè)監(jiān)聽者昆淡,并制定監(jiān)聽的是那種運(yùn)行模式
          /*
           CFRunLoopRef rl  要添加監(jiān)聽者的RunLoop
           CFRunLoopObserverRef observer,  要添加的監(jiān)聽者
           CFStringRef mode  RunLoop的運(yùn)行模式
           */
          CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锰瘸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昂灵,更是在濱河造成了極大的恐慌避凝,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眨补,死亡現(xiàn)場(chǎng)離奇詭異管削,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)撑螺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門含思,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人甘晤,你說我怎么就攤上這事含潘。” “怎么了安皱?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵调鬓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我酌伊,道長(zhǎng)腾窝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任居砖,我火速辦了婚禮虹脯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奏候。我一直安慰自己循集,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布蔗草。 她就那樣靜靜地躺著咒彤,像睡著了一般疆柔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上镶柱,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天旷档,我揣著相機(jī)與錄音,去河邊找鬼歇拆。 笑死鞋屈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的故觅。 我是一名探鬼主播厂庇,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼输吏!你這毒婦竟也來了权旷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤评也,失蹤者是張志新(化名)和其女友劉穎炼杖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盗迟,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坤邪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了罚缕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艇纺。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖邮弹,靈堂內(nèi)的尸體忽然破棺而出黔衡,到底是詐尸還是另有隱情,我是刑警寧澤腌乡,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布盟劫,位于F島的核電站,受9級(jí)特大地震影響与纽,放射性物質(zhì)發(fā)生泄漏侣签。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一急迂、第九天 我趴在偏房一處隱蔽的房頂上張望影所。 院中可真熱鬧,春花似錦僚碎、人聲如沸猴娩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卷中。三九已至矛双,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仓坞,已是汗流浹背背零。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留无埃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓毛雇,卻偏偏與公主長(zhǎng)得像嫉称,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灵疮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

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

  • ======================= 前言 RunLoop 是 iOS 和 OSX 開發(fā)中非持模基礎(chǔ)的一個(gè)...
    i憬銘閱讀 867評(píng)論 0 4
  • 如果沒有RunLoop main函數(shù)中的RunLoop 第14行代碼的UIApplicationMain函數(shù)內(nèi)部就...
    JonesCxy閱讀 526評(píng)論 0 4
  • RunLoop的基本了解 **1 . RunLoop字面的意思 : **運(yùn)行循環(huán) / 跑圈 **2 . 基本作用 ...
    Mario_ZJ閱讀 505評(píng)論 1 3
  • 每次面試荔棉,Runloop這個(gè)概念幾乎是必問的。所以蒿赢,還是寫點(diǎn)東西出來做個(gè)記錄润樱,同時(shí)也加深一下自己的記憶。 一.什么...
    碼農(nóng)老張閱讀 1,629評(píng)論 0 4
  • 基礎(chǔ)知識(shí) 作用 保持程序持續(xù)運(yùn)行 處理app中的各種事件(如觸摸事件羡棵、定時(shí)器事件【NSTimer】壹若、selecto...
    MYS_iOS_8801閱讀 178評(píng)論 0 0