runloop:运行循环。
1.开启一条线程,这条线程就是这个app的主线程。
2.这个主线程是常驻线程(一旦启动,就不再退出),这条线程上的runloop被开启。
程序启动入口函数:main()
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
现在在main函数中执行一个操作
int a = UIApplicationMain(argc, argv, nil, appDelegateClassName);
NSLog(@"执行");
return a;
此时可以发现,log是不会打印的。
由此可见,在app开启的时候,就开启了一个死循环,持续执行。
runloop就是一个死循环
runloop作用:
1.主线程runloop保证程序不退出,子线程runloop保证线程不退出。
2.监听事件:负责监听所有的事件。在iOS中:触摸、时钟、网络事件。
runloop有5种模式(用到的三种)
1.默认模式 NSDefaultRunLoopMode。---------------- 处理网络事件,timer
2.UI模式(runloop优先处理source:触摸事件)UITrackingRunLoopMode -------UI事件
3.NSRunLoopCommonModes 占位模式(并不是runloop真正的模式)
4.初始化模式,不开放给程序员
5.系统内核模式,不开放给程序员
UI模式优先级最高,但是只能被UI事件唤醒。timer加在UI模式下不会进行处理。
在主线程runloop添加timer,timer回调方法中执行耗时操作,会造成程序卡顿。因此:timer中不建议处理耗时的操作(耗时操作需要放在子线程中)
例:
- (void)viewDidLoad {
[super viewDidLoad];
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)timerMethod{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@",[NSThread currentThread]);
}
上边的写法就会造成主线程的卡顿现象发生。所以为了解决这个问题,放在考虑放在子线程中处理。例:
- (void)viewDidLoad {
[super viewDidLoad];
NSThread *thread = [[NSThread alloc]initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}];
[thread start];
}
- (void)timerMethod{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@",[NSThread currentThread]);
}
此时就会发现,在子线程中,执行完代码,子线程被销毁。因此需要考虑线程保活。
思路一:用一个强引用的全局变量指向线程。会发现线程在执行完毕之后,依旧被销毁。不可用。
线程的生命:是由CPU调度的。只和线程的任务有关系,任务执行完成,线程自动销毁。
线程是CPU调度与执行,与OC对象没有关系。
在线程中添加一个死循环,会发现线程不会被销毁,而指向线程的对象也不会被释放。例:
NSThread *thread = [[NSThread alloc]initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
while (true) {
}
}];
此时,定时器的方法依旧不会调用。因为线程上的runloop需要手动调用。只是添加一个死循环保住线程。此时没有死循环依然会正常执行,说明runloop run方法开启了一个死循环。
[[NSRunLoop currentRunLoop] run];
runlopprunlo取消:runloop开启后,无法取消。
因此可以考虑这样做,例:
while (_Finished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.000001]];
}
也可以考虑退出线程:
[NSThread exit];
提示:退出主线程,app不会挂掉,不响应任何操作,子线程会继续执行。
主线程是UI线程,更新UI。
UIKit不是线程安全。因此约定只在主线程访问UI。(多条线程对同一个对象进行操作,就会出现资源抢夺)。
1.主线程与子线程没有任何区别,主线程是系统开启的。
2.主线程启动的时候进行界面渲染等其他操作,主线程挂掉,不再处理界面山任何东西。
timer的方法是事件不是任务
事件和任务的区别:
在线程创建出来的时候执行的代码就是任务,runloop也是任务。runloop处理的任务就是事件。
runloop没有创建方法:runloop是一个懒加载的方法,在第一次调用的时候创建。
source
事件源、输入源
按照函数调用栈source分类:
source0 非系统内核事件
source1 系统内核事件
observer
CFRunloop :CoreFoundation
CoreFoundation内存不由ARC进行管理
create new copy 这类名称开头的函数会在堆区域开辟内存空间。需要进行内存管理。
malloc创建一个指针,申请内存区域。
free 将指针指向的内存置为空