Objective-C基础-RunLoop

1、什么是RunLoop

故名思义就是,运行循环, 在程序运行过程中循环做一些事情。iOS中有2套API来访问和使用RunLoop。

  • Foundation:NSRunLoop
  • CFRunLoopRef:CFRunLoopRef
  • NSRunLoop和CFRunLoopRef都代表着RunLoop对象
  • NSRunLoop是基于CFRunLoopRef的一层OC包装
  • CFRunLoopRef是开源的。
  • https://opensource.apple.com/tarballs/CF

2、RunLoop与线程关系

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
  • 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

3、获取RunLoop对象

  • Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
  • Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象

4、RunLoop相关的类

Core Foundation中关于RunLoop的5个类

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef
typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;          /* locked for accessing mode list */
    __CFPort _wakeUpPort;           // used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

Runloop结构.jpg

4.1 CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop的运行模式
  • 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
  • RunLoop启动时只能选择其中一个Mode,作为currentMode
  • 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
  • 不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
  • 如果Mode里没有任何Source0/Source1/Timer,RunLoop会立马退出
RunloopMode.jpg

iOS 常用3种Mode

  • kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  • kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode

4.2 CFRunLoopObserverRef

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),            //即将进入Loop
    kCFRunLoopBeforeTimers = (1UL << 1), //即将处理Time
    kCFRunLoopBeforeSources = (1UL << 2),//即将处理Source
    kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6), //刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7),             //即将退出Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

添加Observer监听RunLoop的所有状态

- (void)addRunloopObserver {
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopEntry - %@", mode);
                CFRelease(mode);
            }
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"kCFRunLoopBeforeTimers");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"kCFRunLoopBeforeSources");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"kCFRunLoopBeforeWaiting");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"kCFRunLoopAfterWaiting");
                break;
            case kCFRunLoopExit: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopExit - %@", mode);
                CFRelease(mode);
            }
                break;
            default:
                break;
        }
    });
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
    CFRelease(observer);
}

5、RunLoop的运行逻辑

RunLoop的运行逻辑
  • Source0: 触摸事件处理、performSelector:onThread:
  • Source1: 基于Port的线程间通信、系统事件捕捉
  • Timers: NSTimerperformSelector:withObject:afterDelay:
  • Observers: 用于监听RunLoop的状态、UI刷新(BeforeWaiting)、Autorelease poolBeforeWaiting
Runloop运行流程.jpg

6、RunLoop休眠的实现原理

Runloop休眠原理.jpg

7、RunLoop应用

  • 控制线程生命周期(线程保活)
  • 解决NSTimer在滑动时停止工作的问题 (NSRunLoopCommonModes)
  • 监控应用卡顿
  • 性能优化

线程保活

@interface SRThread : NSThread

@end

@implementation SRThread
- (void)dealloc {
    NSLog(@"%s", __func__);
}
@end

@interface ViewController ()
@property (strong, nonatomic) SRThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;
@end

@implementation ViewController

- (void)dealloc {
    NSLog(@"%s", __func__);
    [self stop];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    __weak typeof(self) weakSelf = self;
    
    self.stopped = NO;
    self.thread = [[SRThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);
        
        // 往RunLoop里面添加Source\Timer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        
        while (weakSelf && !weakSelf.isStoped) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        
        NSLog(@"%@----end----", [NSThread currentThread]);
    }];
    [self.thread start];
    self.view.backgroundColor = [UIColor greenColor];
}

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

// 子线程需要执行的任务
- (void)test {
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

- (void)stop {
    if (!self.thread) {
        return;
    }
    
    // 在子线程调用stop(waitUntilDone设置为YES,代表子线程的代码执行完毕后,这个方法才会往下走)
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}

// 用于停止子线程的RunLoop
- (void)stopThread {
    // 设置标记为YES
    self.stopped = YES;
    
    // 停止RunLoop
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    
    // 清空线程
    self.thread = nil;
}

@end

持久线程封装

#import 

/**
 该线程执行完任务不会销毁, 手动调用stop后,线程销毁了
 */
@interface SRPermenantThread : NSObject
/**
 在当前子线程执行一个任务
 */
- (void)executeTask:(dispatch_block_t)task;

/**
 结束线程
 */
- (void)stop;

@end



@interface SRPermenantThread()
@property (strong, nonatomic) NSThread *innerThread;
@end

@implementation SRPermenantThread

- (void)dealloc {
    [self stop];
}

#pragma mark - public methods

- (instancetype)init {
    if (self = [super init]) {
        self.innerThread = [[NSThread alloc] initWithBlock:^{
            // 创建上下文(要初始化一下结构体)
            CFRunLoopSourceContext context = {0};
            
            // 创建source
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
            
            // 往Runloop中添加source
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
            
            // 销毁source
            CFRelease(source);
            
            // 启动
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
        }];
        
        [self.innerThread start];
    }
    return self;
}

- (void)executeTask:(dispatch_block_t)task {
    if (!self.innerThread || !task) return;
    
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)stop {
    if (!self.innerThread) {
        return;
    }
    
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}


#pragma mark - private methods

- (void)__stop {
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThread = nil;
}

- (void)__executeTask:(dispatch_block_t)task {
    task();
}

@end

你可能感兴趣的:(Objective-C基础-RunLoop)