1.系统默认的5个mode
NSDefaultRunLoopMode
这个mode一般是主线程RunLoop的默认mode。创建线程之初RunLoop是以这种mode运行的。
UITrackingRunLoopMode
这个mode是保证滑动ScrollView滑动不受影响,比如滑动tableView的时候主线程就切到这个mode上了。
UITInitializationRunLoopMode
在刚启动App的时候第一次进入的mode,启动后就不进入此mode了,本人理解是为了防止App进入时选择了其他mode而运行错乱。
GSEventReceiveRunLoopMode
接受系统内部的mode,通常不用。
NSRunLoopCommonModes
这个mode是包括1和2的,因此解有个一个问题。定时器触发的时候滑动TableView定时器会停止。这是因为默认是在第一个mode上运行,在滑动的时候RunLoop切换到了第二个mode,所以第一个mode上的任务就被搁置了。
So,解决这个问题就直接把Timer加入到第五个mode中就完美解决了,滑动的时候也不影响定时器的触发。
2.什么时候会创建autoreleasepool?
线程启动runloop后自动生成NSAutoreleasePool接受对象,当当前runloop迭代结束时,释放该pool.
enumerateObjectUsingBlock:,系统在这类快速遍历方法中会自动添加autoreleasepool.
3.什么是自动释放池?
自动释放池是用来存储多个对象类型的指针变量
4.自动释放池对池内对象的作用?
被存入到自动释放池内的对象,当自动释放池被销毁时,会对池内的对象全部做一次release操作
5.自动释放池作用
将对象与自动释放池建立关系,池子内调用 autorelease 方法,在自动释放池销毁时销毁对象,延迟 release 销毁时间
6.对象如何放入到自动释放池中?
MRC当你确定要将对象放入到池中的时候,只需要调用对象的 autorelease 对象方法
就可以把对象放入到自动释放池中.ARC下如果手动的添加的话需要使用__autoreleasing修饰对象.
7.Autoreleasepool 与 Runloop 的关系
主线程默认为我们开启 Runloop,Runloop 会自动帮我们创建Autoreleasepool,
并进行Push、Pop 等操作来进行内存管理
8.ARC 下什么样的对象由 Autoreleasepool 管理
ARC 下什么样的对象由 Autoreleasepool 管理呢?大多数人的回答是:“都会由 pool 进行管理”。
其实并不是这样的,对于普通的对象是由编译器在合适的地方为我们 Realease 了。
可以使用_objc_autoreleasePoolPrint();这个函数打印注册在缓存中的对象
9.runloop和线程的关系
每个线程都会对应一个runloop.
10.autoreleasepool何时释放
当runloop结束的时候(即进入休眠或者销毁时)会释放autoreleasepool
11.线程安全
线程安全指进行线程操作的时候进行加锁操作(购买车票),NSRunLoop不安全,CFRunLoopRef安全
12.线程刚创建的时候不会有runloop,当主动获取时才会有runloop,线程结束时会销毁对应的runloop,示例代码
/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 访问 loopsDic 时的锁
static CFSpinLock_t loopsLock;
/// 获取一个 pthread 对应的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
/// 直接从 Dictionary 里获取。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
/// 取不到时,创建一个
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
/// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
13.App内runloop的个数
个人测试结果是,主线程肯定有一个,当开辟子线程的时候获取currentloop时会再次创建一个,子线程
结束时这个currentloop会销毁.
dispatch_async(dispatch_queue_create("ces", DISPATCH_QUEUE_CONCURRENT), ^{
NSLog(@"打印当前runloop%@",CFRunLoopGetCurrent());
});
14.runloop中sources的分类
Source0 : 触摸事件,PerformSelectors,非基于Port的
Source1 : 基于Port的线程间通信,基于Port的
15.__autoreleasing将变量加入缓存池中,防止在autoreleasepool(缓存池)结束前被释放,延迟释放,不同于默认的__strong变量,没有引用时才会释放。
16.alloc/new/copy/mutableCopy和其他方法的返回值取得的对象,都将注册到autoreleasepool.
17. __autoreleasing加入缓存池,如果显式的使用__autoreleasing那么修饰的需是自动变量(包括局部变量,函数,方法参数)
-(void)test{
NSError * error;
[self testError:&error];
//以上代码在编译时会改变为下面的形式
//NSError __strong* error;
//NSError __autoreleasing *tmp = error;
//[self testError:&tmp];
}
- (void)testError:(NSError * __autoreleasing *)error{
NSError * err = [[NSError alloc]init];
*error = err;
}
网友评论