@whiteiOS
2017-02-23T04:16:09.000000Z
字数 6711
阅读 772
多线程网络

UIApplicationMain函数内部就启动了一个RunLoop,所以UIApplicationMain函数一直没有返回,保证了程序的持续运行
int main(int argc, char * argv[]) {@autoreleasepool {return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));}}
UIApplicationMain函数返回值为int类型,main函数做以下修改,结果是number一直没有打印
int main(int argc, char * argv[]) {@autoreleasepool {NSLog(@"%s", __func__);int number = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));NSLog(@"%d", number);return number;}}
//创建子线程对应的RunLoop,currentRunLoop是懒加载的//该方法既能创建子线程的RunLoop,也能获取子线程的RunLoopNSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];//子线程的RunLoop还必须手动开启[currentRunloop run];
iOS中有两套API来访问和使用RunLoop
NSRunLoopCFRunLoopRefCFRunLoopRef是用C语言写的,而NSRunLoop是基于CFRunLoopRef进行的OC包装,所以CFRunLoopRef更底层,性能会高一点Foundation获取RunLoop对象
//获取主线程的RunLoop对象[NSRunLoop mainRunLoop];//获取当前线程的RunLoop对象[NSRunLoop currentRunLoop];
//获取主线程的RunLoop对象CFRunLoopGetMain();//获取当前线程的RunLoop对象CFRunLoopGetCurrent();
Core Foundation中关于RunLoop的5个类
几个类的关系如下图:

但是,每次RunLoop启动时,只能选择一个运行模式(Mode),这个Mode被称作CurrentMode;如果需要切换Mode,只能退出RunLoop,再重新选择一个Mode然后进入;这样做注意是为了分隔开不同组的Source/Timer/Observer,让其互不影响一个Mode中必须要有Source或者Timer,否则RunLoop将不会运行
//默认Mode,通常主线程是在这个Mode下运行kCFRunLoopDefaultMode;//界面追踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响//如果界面在滑动scrollView或者tableView或者textView等,会自动进入该模式UITrackingRunLoopMode;//在刚启动APP时进入的第一个Mode,启动完成后就不在使用UIInitializationRunLoopMode;//接受系统时间的内部Mode,通常用不到GSEventReceiveRunLoopMode;//这个一个占位用的Mode,不是一种真正的ModekCFRunLoopCommonModes;
①情况一:使用timerWithTimeInterval:创建NSTimer
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
timerWithTimeInterval:创建NSTimer之后,必须要将timer添加到RunLoop中,可以设置RunLoop的运行模式为NSDefaultRunLoopMode②情况二:使用timerWithTimeInterval:创建NSTimer,并且在界面添加一个tableView控件
UITrackingRunLoopMode(界面追踪模式),而此时在NSDefaultRunLoopMode下的定时器就会停止工作;而当停止滑动tableView的时候,RunLoop又重新回到NSDefaultRunLoopMode,这时定时器才会重新工作NSRunLoopCommonModes;该做法意味这将定时器既添加到了UITrackingRunLoopMode模式下,又添加到了NSDefaultRunLoopMode模式下注意:
NSRunLoopCommonModes并不是一种真正的运行模式,它类似于一个标签,而UITrackingRunLoopMode和NSDefaultRunLoopMode这两种运行模式都带有NSRunLoopCommonModes这个标签。怎么验证呢?通过打印当前的RunLoop对象,如下图。凡是添加到NSRunLoopCommonModes中的事件都会同时添加到带有common标签的运行模式上,所以定时器才会在两种模式下都能正常运行

③情况三:使用scheduledTimerWithTimeInterval:创建定时器
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
NSDefaultRunLoopMode由此可见,NSTimer依赖于RunLoop的运行模式;另外,如果在子线程中直接创建NSTimer是不行的,因为NSTimer要添加到当前的RunLoop中,而在子线程中默认情况下是不存在RunLoop的,还需要自己创建子线程的RunLoop

- (void)GCDTimer{/*作用:1.创建GCD中的定时器参数一:DISPATCH_SOURCE_TYPE_TIMER说明是定时器参数二:描述信息参数三:更详细的描述信息参数四:队列,决定GCD定时器的任务在哪个线程中执行*/dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));/*作用:2.设置定时器参数一:timer-定时器对象参数二:起始时间,DISPATCH_TIME_NOW-从现在开始计时参数三:间隔时间(GCD中的时间单位为纳秒)参数四:精准度,0表示绝对精准*/dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);//3.设置定时器执行的任务dispatch_source_set_event_handler(timer, ^{NSLog(@"%@", [NSThread currentThread]);});//4.启动执行dispatch_resume(timer);//5.此时定时器还不会启动,因为timer只是局部变量,当2秒后开始执行的时候可能timer已经被释放掉了,所以需要添加一个属性对timer进行强引用self.timer = timer;}@property (nonatomic, strong) dispatch_source_t timer;
函数调用栈来分的,如何查看函数调用栈?在控制器界面拖一个button,拖线实现按钮点击方法,然后在方法内部打一个断点,点击下图红框部分

函数调用栈,运行顺序为从下往上,可以看到按钮的点击为source0事件
- (void)runLoopObserver{/*作用:1.创建监听者参数一:怎么分配内存空间参数二:要监听的RunLoop的状态参数三:是否持续监听参数四:优先级,总是传0参数五:当状态改变时候的回调*/CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {/*RunLoop的所有状态kCFRunLoopEntry = (1UL << 0), 即将进入RunLoopkCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件kCFRunLoopBeforeSources = (1UL << 2), 即将处理source事件kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入睡眠kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒kCFRunLoopExit = (1UL << 7), RunLoop退出kCFRunLoopAllActivities = 0x0FFFFFFFU RunLoop所有状态*/switch (activity) {case kCFRunLoopEntry:NSLog(@"即将进入RunLoop");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.把观察者添加到RunLoop上参数一:要监听哪个RunLoop参数二:观察者参数三:运行模式*/CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);}


@interface ViewController ()/** 注释 */@property (nonatomic, strong) NSThread *thread;@end//按钮一:创建子线程- (IBAction)createThread{self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(task1) object:nil];[self.thread start];}- (void)task1{NSLog(@"task1---%@",[NSThread currentThread]);}//按钮二:让刚才子线程继续工作- (IBAction)goOnWorking{[self performSelector:@selector(task2) onThread:self.thread withObject:nil waitUntilDone:YES];}- (void)task2{NSLog(@"task2---%@",[NSThread currentThread]);}
- (void)task1{//1.获得子线程对应的RunLoopNSRunLoop *runLoop = [NSRunLoop currentRunLoop];//2.开启RunLoop[runLoop run];}
一个运行模式中还必须要有source或者timer,所以必须添加一个timer或者source
- (void)task1{//1.获得子线程对应的RunLoopNSRunLoop *runLoop = [NSRunLoop currentRunLoop];//2.给RunLoop添加source[runLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];//3.开启RunLoop[runLoop run];}