你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS CFRUnLoopRef 之CFRunLoopMode

iOS CFRUnLoopRef 之CFRunLoopMode

編輯:IOS開發綜合

1.簡介:

每個CFRunLoopRef 包含若干個 Mode,每個 Mode 又包含若干個 Source/Timer/Observer。每次調用 RunLoop 的主函數時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個 Mode 進入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。

CFRunLoopModeRef 類並沒有對外暴露,只是通過 CFRunLoopRef 的接口進行了封裝。他們的關系如下:

CFRunLoopRef獲取Mode的接口:

CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);
CFRunLoopRunInMode(CFStringRef modeName, ...);

2.CFRunLoopMode的類型

kCFRunLoopDefaultMode
App的默認Mode,通常主線程是在這個Mode下運行

UITrackingRunLoopMode:
界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響

UIInitializationRunLoopMode:
在剛啟動 App 時第進入的第一個 Mode,啟動完成後就不再使用 GSEventReceiveRunLoopMode:
接受系統事件的內部 Mode,通常用不到 kCFRunLoopCommonModes:
這是一個占位用的Mode,作為標記kCFRunLoopDefaultMode和UITrackingRunLoopMode用,並不是一種真正的Mode

3.CFRunLoopMode 和 CFRunLoop 的結構

struct __CFRunLoopMode {
    CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;    // Set 
    CFMutableSetRef _sources1;    // Set 
    CFMutableArrayRef _observers; // Array 
    CFMutableArrayRef _timers;    // Array 
    ...
};

struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set 
    CFMutableSetRef _commonModeItems; // Set 
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set 
    ...
};

4. CommonModes(很實際問題)

一個 Mode 可以將自己標記為”Common”屬性(通過將其 ModeName 添加到 RunLoop 的 “commonModes” 中)。RunLoop 會自動將 _commonModeItems 加入到具有 “Common” 標記的所有Mode裡。 應用場景舉例:主線程的 RunLoop 裡有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。這兩個 Mode 都已經被標記為”Common”屬性。DefaultMode 是 App 平時所處的狀態,TrackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態。當你創建一個 Timer 並加到 DefaultMode 時,Timer 會得到重復回調,但此時滑動一個TableView時,RunLoop 會將 mode 切換為 TrackingRunLoopMode,這時 Timer 就不會被回調,並且也不會影響到滑動操作。 有時你需要一個 Timer,在兩個 Mode 中都能得到回調,一種辦法就是將這個 Timer 分別加入這兩個 Mode。還有一種方式,就是將 Timer 加入到頂層的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自動更新到所有具有”Common”屬性的 Mode 裡去。 有時你需要一個 Timer,在兩個 Mode 中都能得到回調,一種辦法就是將這個 Timer 分別加入這兩個 Mode。還有一種方式,就是將 Timer 加入到頂層的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自動更新到所有具有”Common”屬性的 Mode 裡去。

5.Mode之間的切換

理解了CommonModes,那麼下面的只能算是具體應用,以便加深理解

我們平時在開發中一定遇到過,當我們使用NSTimer每一段時間執行一些事情時滑動UIScrollView,NSTimer就會暫停,當我們停止滑動以後,NSTimer又會重新恢復的情況,我們通過一段代碼來看一下:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
    // 加入到RunLoop中才可以運行
    // 1. 把定時器添加到RunLoop中,並且選擇默認運行模式
NSDefaultRunLoopMode = kCFRunLoopDefaultMode
    // [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    // 當textFiled滑動的時候,timer失效,停止滑動時,timer恢復
    // 原因:當textFiled滑動的時候,RunLoop的Mode會自動切換成UITrackingRunLoopMode模式,因此timer失效,當停止滑動,RunLoop又會切換回NSDefaultRunLoopMode模式,因此timer又會重新啟動了

    // 2. 當我們將timer添加到UITrackingRunLoopMode模式中,此時只有我們在滑動textField時timer才會運行
    // [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

    // 3. 那個如何讓timer在兩個模式下都可以運行呢?
    // 3.1 在兩個模式下都添加timer 是可以的,但是timer添加了兩次,並不是同一個timer
    // 3.2 使用站位的運行模式 NSRunLoopCommonModes標記,凡是被打上NSRunLoopCommonModes標記的都可以運行,下面兩種模式被打上標簽
    //0 : {contents = "UITrackingRunLoopMode"}
    //2 : {contents = "kCFRunLoopDefaultMode"}
    // 因此也就是說如果我們使用NSRunLoopCommonModes,timer可以在UITrackingRunLoopMode,kCFRunLoopDefaultMode兩種模式下運行
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    NSLog(@"%@",[NSRunLoop mainRunLoop]);
}
-(void)show
{
    NSLog(@"-------");
}

由上述代碼可以看出,NSTimer不管用是因為Mode的切換,因為如果我們在主線程使用定時器,此時RunLoop的Mode為kCFRunLoopDefaultMode,即定時器屬於kCFRunLoopDefaultMode,那麼此時我們滑動ScrollView時,RunLoop的Mode會切換到UITrackingRunLoopMode,因此在主線程的定時器就不在管用了,調用的方法也就不再執行了,當我們停止滑動時,RunLoop的Mode切換回kCFRunLoopDefaultMode,所有NSTimer就又管用了。
同樣道理的還有ImageView的顯示,如下:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"%s",__func__);
    // performSelector默認是在default模式下運行,因此在滑動ScrollView時,圖片不會加載
    // [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"abc"] afterDelay:2.0 ];
    // inModes: 傳入Mode數組
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"abc"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode,UITrackingRunLoopMode]];

6. CFRunLoopMode 的item

Mode中的 Source/Timer/Observer 被統稱為 mode item,一個 item 可以被同時加入多個 mode。但一個 item 被重復加入同一個 mode 時是不會有效果的。如果一個 mode 中一個 item 都沒有,則 RunLoop 會直接退出,不進入循環。

Mode 暴露的管理 mode item 的接口有下面幾個:

CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode)
你只能通過 mode name 來操作內部的 mode,當你傳入一個新的 mode name 但 RunLoop 內部沒有對應 mode 時,RunLoop會自動幫你創建對應的 CFRunLoopModeRef。對於一個 RunLoop 來說,其內部的 mode 只能增加不能刪除。 蘋果公開提供的 Mode 有兩個:kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode,你可以用這兩個 Mode Name 來操作其對應的 Mode。 同時蘋果還提供了一個操作 Common 標記的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes),你可以用這個字符串來操作 Common Items,或標記一個 Mode 為 “Common”。使用時注意區分這個字符串和其他 mode name。
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved