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中任何一个模式,这个事件源均可以被触发。