iOS专题资源__系统知识点iOS RunLoopruntime相关

iOS重学之路--Runloop

2016-06-23  本文已影响614人  摄影师诺风

这就是一片文言文,请各位客官耐着性子去看。

苹果官方文档

CFRunLoopRef开源代码下载地址

    1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程
        
    2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建
    3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁
1.获得当前Runloop对象
    //01 NSRunloop
     NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
    //02 CFRunLoopRef
    CFRunLoopRef runloop2 =   CFRunLoopGetCurrent();

2.拿到当前应用程序的主Runloop(主线程对应的Runloop)
    //01 NSRunloop
     NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];
    //02 CFRunLoopRef
     CFRunLoopRef runloop2 =   CFRunLoopGetMain();

3.注意点:开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。
4.在子线程中,如果不主动获取Runloop的话,那么子线程内部是不会创建Runloop的。可以下载CFRunloopRef的源码,搜索_CFRunloopGet0,查看代码。
5.Runloop对象是利用字典来进行存储,而且key是对应的线程,Value为该线程对应的Runloop。

(1)Runloop运行原理图


2.png

上图显示了线程的输入源

1.基于端口的输入源(Port Sources)
2.自定义输入源(Custom Sources)
3.Cocoa执行Selector的源(performSelectorxxxx方法)
4.定时源(Timer Sources )
线程针对上面不同的输入源,有不同的处理机制

1.handlePort——处理基于端口的输入源
2.customSrc——处理用户自定义输入源
3.mySelector——处理Selector的源
4.timerFired——处理定时源

(2)五个相关的类

a.CFRunloopRef
b.CFRunloopModeRef【Runloop的运行模式】
c.CFRunloopSourceRef【Runloop要处理的事件源】
d.CFRunloopTimerRef【Timer事件】
e.CFRunloopObserverRef【Runloop的观察者(监听者)】

(3)Runloop和相关类之间的关系图

1.png

(4)Runloop要想跑起来,它的内部必须要有一个mode,这个mode里面必须有source\timer,至少要有其中的一个。(因为source\timer是主动发消息过来的,有信号源可以监听,但是observer是被动监听的,它不是主动的,因此,mode 加这个Runloop是跑步起来的)

- (void)viewDidLoad {
    [super viewDidLoad];
    
//    NSLog(@"%@", [NSRunLoop currentRunLoop]);
    
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(execute) object:nil];
    self.thread = thread;
        [thread start];
}

- (void)execute
{
    /** 当你为此时创建的runloop添加observer时,它并不能运行起来(线程还是会死掉的)
     //        [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
     //    [NSRunLoop currentRunLoop] addTimer:<#(nonnull NSTimer *)#> forMode:<#(nonnull NSString *)#>
     只有你添加了以上两中方式才能确保runloop存在,线程不死*/
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"----");
    });
    
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    
    CFRelease(observer);
//
    //        [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    //    [NSRunLoop currentRunLoop] addTimer:<#(nonnull NSTimer *)#> forMode:<#(nonnull NSString *)#>
    [[NSRunLoop currentRunLoop] run];
    
    NSLog(@"------1111");
}
- (void)test
{
    NSLog(@"----------test----%@", [NSThread currentThread]);
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

(1)NSTimer相关代码

/*
    说明:
    (1)runloop一启动就会选中一种模式,当选中了一种模式之后就不鸟其它的模式。一个mode里面可以添加多个NSTimer,也就是说以后当创建NSTimer的时候,可以指定它是在什么模式下运行的。
    (2)它是基于时间的触发器,说直白点那就是时间到了我就触发一个事件,触发一个操作。基本上说的就是NSTimer
    
*/
- (void)timer2
{
    //NSTimer 调用了scheduledTimer方法,那么会自动添加到当前的runloop里面去,而且runloop的运行模式kCFRunLoopDefaultMode

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //更改模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

}
- (void)timer1
{
    //    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //定时器添加到UITrackingRunLoopMode模式,一旦runloop切换模式,那么定时器就不工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

    //定时器添加到NSDefaultRunLoopMode模式,一旦runloop切换模式,那么定时器就不工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    //占位模式:common modes标记
    //被标记为common modes的模式 kCFRunLoopDefaultMode  UITrackingRunLoopMode
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

(1)CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

(2)如何监听

 //创建一个runloop监听者
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

        NSLog(@"监听runloop状态改变---%zd",activity);


       switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"runloop进入");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"runloop要去处理timer");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"runloop要去处理Sources");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"runloop要睡觉了");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"runloop醒来啦");
                break;

            case kCFRunLoopExit:
                NSLog(@"runloop退出");
                break;
            default:
                break;
        }
    });

    //为runloop添加一个监听者
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

    CFRelease(observer);

(3)监听的状态

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),   //即将进入Runloop
    kCFRunLoopBeforeTimers = (1UL << 1),    //即将处理NSTimer
    kCFRunLoopBeforeSources = (1UL << 2),   //即将处理Sources
    kCFRunLoopBeforeWaiting = (1UL << 5),   //即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),    //刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7),            //即将退出runloop
    kCFRunLoopAllActivities = 0x0FFFFFFFU   //所有状态改变
};
3.png 4.png

(二)自动释放池

自动释放池什么时候释放?


通过Observer监听RunLoop的状态,一旦监听到RunLoop即将进入睡眠等待状态,就释放自动释放池(kCFRunLoopBeforeWaiting)
kCFRunLoopEntry; // 创建一个自动释放池
kCFRunLoopBeforeWaiting; // 销毁自动释放池,创建一个新的自动释放池
kCFRunLoopExit; // 销毁自动释放池

/*
 kCFRunLoopEntry = (1UL << 0),  1
 kCFRunLoopBeforeTimers = (1UL << 1), 2
 kCFRunLoopBeforeSources = (1UL << 2), 4
 kCFRunLoopBeforeWaiting = (1UL << 5), 32
 kCFRunLoopAfterWaiting = (1UL << 6), 64
 kCFRunLoopExit = (1UL << 7), 128
 */

// activities = 0x1 == 1
// activities = 0xa0 == 160

//<CFRunLoopObserver 0x7fe6a3611c50 [0x10a417a40]>{valid = Yes, activities = 0x1,
 repeats = Yes, order = -2147483647,
 callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10a59e4c2),
 context = <CFArray 0x7fe6a3611ae0 [0x10a417a40]>{type = mutable-small, count = 1,
 values = ( 0 : <0x7fe6a4800048> )}}

//<CFRunLoopObserver 0x7fe6a3611d30 [0x10a417a40]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10a59e4c2), context = <CFArray 0x7fe6a3611ae0 [0x10a417a40]>{type = mutable-small, count = 1, values = (
//                                                                                                                                                                                                                                                                                0 : <0x7fe6a4800048>
//                                                                                                                                                                                                                                                                                )}}

/** 查看上面的activities   发现 监听的是下面三个值   0xa0 == 160
 kCFRunLoopBeforeWaitin|kCFRunLoopExit == 160
 */

/**
kCFRunLoopEntry = (1UL << 0),  1
+
kCFRunLoopBeforeWaiting = (1UL << 5), 32
kCFRunLoopExit = (1UL << 7), 128
*/

不太好的demo,请戳这里

上一篇下一篇

猜你喜欢

热点阅读