AFN
AFN2.x中把網絡請求所有都放在一個子線程中進行。因爲子線程運行完任務後就會自動銷燬,因此在子線程中運行了一個Runloop保證線程不會被銷燬掉。(線程的建立和銷燬耗費的資源雖然不多,可是大量網絡請求致使大量建立和銷燬所耗費的資源仍是十分可觀的)javascript
#pragma mark AFN
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}複製代碼
用CFRunloop也可創建一個Runloopjava
CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
//A perform callback for the run loop source. This callback is called when the source has fired.
//Availability
context.perform = fire;
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
NSLog(@"自定義RunLoopRun");
CFRunLoopRun();複製代碼
僅當在爲你的程序建立輔助線程的時候,你才須要顯式運行一個run loop。
Run loop在你要和線程有更多的交互時才須要,好比如下狀況:
>編程
Run Loop的處理兩大類事件源:Timer Source和Input Source(包括performSelector* 方法簇、Port或者自定義Input Source),每一個事件源都會綁定在Run Loop的某個特定模式mode上,並且只有RunLoop在這個模式運行的時候纔會觸發該Timer和Input Source。xcode
Example:安全
void createPortSource()
{
CFMessagePortRef port = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("com.someport"),myCallbackFunc, NULL, NULL);
CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
while (pageStillLoading) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CFRunLoopRun();
[pool release];
}
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}複製代碼
void createCustomSource()
{
CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
while (pageStillLoading) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CFRunLoopRun();
[pool release];
}
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}複製代碼
Cocoa的Selector源網絡
定時源多線程
//方法一:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4.0
target:self
selector:@selector(backgroundThreadFire:) userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timerforMode:NSDefaultRunLoopMode];
//方法二:
[NSTimer scheduledTimerWithTimeInterval:10
target:self
selector:@selector(backgroundThreadFire:)
userInfo:nil
repeats:YES];複製代碼
源是在合適的同步或異步事件發生時觸發,而run loop觀察者則是在run loop自己運行的特定時候觸發。你可使用run loop觀察者來爲處理某一特定事件或是進入休眠的線程作準備。你能夠將run loop觀察者和如下事件關聯:異步
每次運行run loop,你線程的run loop對會自動處理以前未處理的消息,並通知相關的觀察者。具體的順序以下:函數
由於定時器和輸入源的觀察者是在相應的事件發生以前傳遞消息,因此通知的時間和實際事件發生的時間之間可能存在偏差。若是須要精確時間控制,你可使用休眠和喚醒通知來幫助你校對實際發生事件的時間。oop
由於當你運行run loop時定時器和其它週期性事件常常須要被傳遞,撤銷run loop也會終止消息傳遞。典型的例子就是鼠標路徑追蹤。由於你的代碼直接獲取到消息而不是經由程序傳遞,所以活躍的定時器不會開始直到鼠標追蹤結束並將控制權交給程序。
Run loop能夠由run loop對象顯式喚醒。其它消息也能夠喚醒run loop。例如,添加新的非基於端口的源會喚醒run loop從而能夠當即處理輸入源而不須要等待其餘事件發生後再處理。
從這個事件隊列中能夠看出:
①若是是事件到達,消息會被傳遞給相應的處理程序來處理, runloop處理完當次事件後,run loop會退出,而無論以前預約的時間到了沒有。你能夠從新啓動run loop來等待下一事件。
②若是線程中有須要處理的源,可是響應的事件沒有到來的時候,線程就會休眠等待相應事件的發生。這就是爲何run loop能夠作到讓線程有工做的時候忙於工做,而沒工做的時候處於休眠狀態。
何時使用run loop
僅當在爲你的程序建立輔助線程的時候,你才須要顯式運行一個run loop。Run loop是程序主線程基礎設施的關鍵部分。因此,Cocoa和Carbon程序提供了代碼運行主程序的循環並自動啓動run loop。IOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)做爲程序啓動步驟的一部分,它在程序正常啓動的時候就會啓動程序的主循環。相似的,RunApplicationEventLoop函數爲Carbon程序啓動主循環。若是你使用xcode提供的模板建立你的程序,那你永遠不須要本身去顯式的調用這些例程。
對於輔助線程,你須要判斷一個run loop是不是必須的。若是是必須的,那麼你要本身配置並啓動它。你不須要在任何狀況下都去啓動一個線程的run loop。好比,你使用線程來處理一個預先定義的長時間運行的任務時,你應該避免啓動run loop。
若是你決定在程序中使用run loop,那麼它的配置和啓動都很簡單。和全部線程編程同樣,你須要計劃好在輔助線程退出線程的情形。讓線程天然退出每每比強制關閉它更好。
CFRunLoopSourceRef
CFRunLoopTimerRef 是基於時間的觸發器
CFRunLoopObserverRef 是觀察者
Run Loop運行接口
要操做Run Loop,Foundation層和Core Foundation層都有對應的接口能夠操做Run Loop:
Foundation層對應的是NSRunLoop,Core Foundation層對應的是CFRunLoopRef;
兩組接口差很少,不過功能上仍是有許多區別的:
例如CF層能夠添加自定義Input Source事件源、(CFRunLoopSourceRef)Run Loop觀察者Observer(CFRunLoopObserverRef),不少相似功能的接口特性也是不同的。
NSRunLoop的運行接口:
//運行 NSRunLoop,運行模式爲默認的NSDefaultRunLoopMode模式,沒有超時限制
- (void)run;
//運行 NSRunLoop: 參數爲運時間期限,運行模式爲默認的NSDefaultRunLoopMode模式
- (void)runUntilDate:(NSDate *)limitDate;
//運行 NSRunLoop: 參數爲運行模式、時間期限,返回值爲YES表示是處理事件後返回的,NO表示是超時或者中止運行致使返回的
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;複製代碼
Run Loop運行時只能以一種固定的模式運行,若是咱們須要它切換模式,只有停掉它,再從新開啓它。運行時它只會監控這個模式下添加的Timer Source和Input Source,若是這個模式下沒有相應的事件源,Run Loop的運行也會馬上返回的。注意Run Loop不能在運行在NSRunLoopCommonModes模式,由於NSRunLoopCommonModes實際上是個模式集合,而不是一個具體的模式,我能夠在添加事件源的時候使用NSRunLoopCommonModes,只要Run Loop運行在NSRunLoopCommonModes中任何一個模式,這個事件源均可以被觸發。