iOS系列(LJ)iOS开发技术集合Runloop

RunLoop

2015-07-14  本文已影响3670人  YotrolZ

一、RunLoop基本概念

RunLoop伪代码.png

二、RunLoop对象

三、RunLoop与线程

四、RunLoop的结构(为更深入的了解RunLoop我们研究CFRunLoop)

RunLoop结构.png

1. CFRunLoopModeRef:

2. CFRunLoopTimerRef

// 创建一个定时器(NSDefaultRunLoopMode)
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// NSDefaultRunLoopMode:NSTimer只有在默认模式下(NSDefaultRunLoopMode)工作,切换到其他模式不再工作,比如拖拽了界面上的某个控件(会切换成UITrackingRunLoopMode)
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        
// 创建一个定时器(UITrackingRunLoopMode)
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// 拖拽UI界面时出发定时器,在默认模式(NSDefaultRunLoopMode)下不工作
[[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
       
// 创建一个定时器(NSRunLoopCommonModes)
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// NSRunLoopCommonModes仅仅是标记NSTimer在两种模式(UITrackingRunLoopMode/NSDefaultRunLoopMode)下都能运行,但一个RunLoop中同一时间内只能运行一种模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

// 默认已经添加到主线程中RunLoop(mainRunLoop)中(Mode:NSDefaultRunLoopMode)
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
/** 定时器对象 */
@property (nonatomic, strong)dispatch_source_t timer; // 需要一个强引用

NSLog(@"开始");
// 获取队并发队列,定时器的回调函数将会在子线程中执行
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
// 获取主队列,定时器的回调函数将会在子线程中执行
dispatch_queue_t queue = dispatch_get_main_queue();

self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
// 该时间表示从现在开始推迟两秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
    
// 设置定时器的开始时间,间隔时间
dispatch_source_set_timer(self.timer, start, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"------%@", [NSThread currentThread]);
    });
dispatch_resume(self.timer);

/* 参数说明:
// 设置定时器的一些属性
    dispatch_source_set_timer(dispatch_source_t source, // 定时器对象
                              dispatch_time_t start, // 定时器开始执行的时间
                              uint64_t interval, // 定时器的间隔时间
                              uint64_t leeway // 定时器的精度
                              );

*/

3. CFRunLoopSourceRef

4. CFRunLoopObserverRef

// 第一个参数用于分配该observer对象的内存
// 第二个参数用以设置该observer所要关注的的事件,详见回调函数myRunLoopObserver中注释
// 第三个参数用于标识该observer是在第一次进入run loop时执行还是每次进入run loop处理时均执行
// 第四个参数用于设置该observer的优先级,一般为0
// 第五个参数用于设置该observer的回调函数
// 第六个参数observer的运行状态   
 CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            NSLog(@"----监听到RunLoop状态发生改变---%zd", activity);
            });
- 2.将观察者CFRunLoopObserverRef添加到RunLoop上面

        CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

- 3.观察者CFRunLoopObserverRef要`手动释放`
        CFRelease(observer);

五、RunLoop的处理逻辑

RunLoop处理逻辑-官方.png

六、RunLoop的具体使用

- (void)viewDidLoad {
   [super viewDidLoad];
   // 只在NSDefaultRunLoopMode下执行(刷新图片)
   [self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"0"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode]];    
}
#import "ViewController.h"
#import "YCThread.h"
@interface ViewController ()

/*
思路:用一个强引用线程,当点击屏幕的时候再让他启动,结果是不可行!!!!

因为,线程执行完内部的任务后就会自动死亡,你如果用一个强引用引用这个线程,
即使内存还在,但是该线程也已经处于死亡状态(线程状态),是不能再次启动的,
如果再次启动一个死亡状态的线程,就会
报错--reason: '*** -[YCThread start]: attempt(视图) to start the thread again'
*/
/** 线程对象 */
@property (nonatomic, strong)YCThread *thread; // 强引用

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 创建子线程
    self.thread = [[YCThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    // 启动子线程
    [self.thread start];
}

- (void)run {
    NSLog(@"----------");
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // 点击屏幕再次启动线程
    [self.thread start];
}

@end
#import "ViewController.h"

@interface ViewController ()
/** 线程对象 */
@property (nonatomic, strong)NSThread *thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 创建子线程
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    
    [self.thread start];
}

- (void)run {

    NSLog(@"run--%@", [NSThread currentThread]);
    
    // 利用死循环(不建议此做法)
    while (1) {
        [[NSRunLoop currentRunLoop] run];
    }
}

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

    NSLog(@"test--%@", [NSThread currentThread]);
}

@end
#import "ViewController.h"
/*
思路:为了保证线程不死,我们考虑在子线程中加入RunLoop,
    但是由于RunLoop中没有没有源,就会自动退出RunLoop,
    所以我们要为子线程添加一个RunLoop,
    并且为这个RunLoop添加源(保证RunLoop不退出)
*/
@interface ViewController ()

/** 线程对象 */
@property (nonatomic, strong)NSThread *thread;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 创建子线程
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

    //启动子线程
    [self.thread start];
    
}

- (void)run {

    NSLog(@"run--%@", [NSThread currentThread]); // 子线程
    
    // 给子线程添加一个RunLoop,并且加入源
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    // 启动RunLoop
    [[NSRunLoop currentRunLoop] run];
    
    NSLog(@"------------"); // RunLoop启动,这句没有执行的机会
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    // 在子线程中调用test方法,如果子线程还在就能够调用成功
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];
}

- (void)test {
    NSLog(@"test--%@", [NSThread currentThread]); // 子线程
}

@end
上一篇 下一篇

猜你喜欢

热点阅读