NSRunLoop

Runloop作用: 主线程Runloop默认开启,子线程的Runloop需要手动开启。 
常驻线程,Crash的程序回光返照, 在tracking mode下不加载图片等等。

与Runloop有关的5种类型:
CFRunLoopRef:
CFRunLoopModeRef: 运行模式
CFRunLoopTimerRef:定时器时间或者生命周期
CFRunLoopSourceRef: 输入源或者时间源
CFRunLoopObservalRef: 观察者。

1. CFRunLoopModeRef
kCFRunLoopDefaultMode, 默认的mode,通常主线程在这个mode下运行。
kCFRunLoopCommonModes,占位mode,通常标记defaultMode和CommonMode用。
UITrackingRunLoopMode,    追踪mode, 保证scrollView滑动顺畅,不受其它mode影响。
UIInitializationRunLoopMode,  启动程序后的过度mode,启动完成后不再使用。
GSEventReceiveRunLoopMode, Graphic事件相关的mode。

程序崩溃时的回光返照
#import <UIKit/UIKit.h>
#import <signal.h>

@interface FFExceptionCaptureHandle : NSObject
+ (instancetype)share;
- (void)exercute:(NSString *)str;
@end


#import "FFExceptionCaptureHandle.h"


/**
 系统未捕获的异常,如下标越界,数组中插入nil, 重复释放等等

 @param ception 异常信息
 */
void handleException(NSException *ception) {
    NSLog(@"111");
    NSString *name = ception.name;
    NSString *reasion = ception.reason;
    NSDictionary *userInfo = ception.userInfo;
    NSString *userInfoStr = [NSString stringWithFormat:@"name=%@
reasion=%@
userInfo=", name, reasion];
    if (userInfo) {
        for (NSString *key in userInfo.allKeys) {
            NSString *temp = [NSString stringWithFormat:@"%@=%@", key, userInfo[key]];
            userInfoStr = [userInfoStr stringByAppendingString:temp];
        }
    }
    
    [[FFExceptionCaptureHandle share] exercute:userInfoStr];
}

/**
 系统直接发出的异常signal

 @param signal 信号类型
 */
void signalHandle(int signal) {
    NSString * userinfoStr = [NSString stringWithFormat:@"系统发送异常信号 = %d", signal];
    [[FFExceptionCaptureHandle share] exercute:userinfoStr];
}

//void


@implementation FFExceptionCaptureHandle

+ (instancetype)share{
    static FFExceptionCaptureHandle *handle = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        handle = [super allocWithZone:nil];
        [handle FFSetExceptionCapture];
    });
    return handle;
}

- (void)FFSetExceptionCapture {
    NSSetUncaughtExceptionHandler(&handleException);
    signal(SIGABRT, signalHandle);
    signal(SIGILL, signalHandle);
    signal(SIGSEGV, signalHandle);
    signal(SIGFPE, signalHandle);
    signal(SIGBUS, signalHandle);
    signal(SIGPIPE, signalHandle);
}


/**
 接受到异常时候的处理

 @param str 异常信息
 */
- (void)exercute:(NSString *)str {
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    NSArray *allModels = CFBridgingRelease(CFRunLoopCopyAllModes(runloop));
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"程序崩溃了" message:str delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:nil];
    [alertView show];
    
    while (1) {
        for (NSString *model in allModels) {
            CFRunLoopRunInMode((CFStringRef)model, 0.001, false);
        }
    }
}

+ (id)allocWithZone:(struct _NSZone *)zone {
    return [self share];
}

- (id)mutableCopyWithZone:(NSZone *)zone{
    return [FFExceptionCaptureHandle share];
}

- (id)copyWithZone:(NSZone *)zone {
    return [FFExceptionCaptureHandle share];
}

@end
 1 - (void)addThrea
 2 {
 3     NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(createRunLoop) object:nil
 4                         ];
 5     /// 线程名称
 6     thread.name = @"xxx";
 7     /// 开始线程
 8     [thread start];
 9     /// 防止thread被立即销毁。
10     self.thread = thread;
11 }
12 
13 - (void)createRunLoop
14 {
15     /// 主线程中的RunLoop会自动生成,子线程中的RunLoop在调用currentRunLoop时生成.
16     /// 防止runloop被销毁。
17     self.runloop = [NSRunLoop currentRunLoop];
18     /// runloop的context
19     CFRunLoopObserverContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
20     /// runloop的observer
21     CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
22     if (observer) {
23         CFRunLoopAddObserver([_runloop getCFRunLoop], observer, kCFRunLoopDefaultMode);
24     }
25     /// 添加事件, 在两秒执行完后,runloop退出
26     [self performSelector:@selector(handleEvent:) withObject:@"测试" afterDelay:2];
27     /// 添加端口事件保证保证runloop长驻
28     [_runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
29     ///
30     [self addSourceEvent];
31     /// 退出runloop
32     [_runloop run];
33     
34 }
35 
36 /// runloop观察者回调
37 void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
38 {
39     if (activity & kCFRunLoopEntry) {
40         NSLog(@"调用runloop");
41     }else if (activity & kCFRunLoopBeforeTimers) {
42         NSLog(@"即将处理计时器事件");
43     }else if (activity & kCFRunLoopBeforeSources) {
44         NSLog(@"即将处理source事件 ");
45     }else if (activity & kCFRunLoopBeforeWaiting) {
46         NSLog(@"即将休眠");
47     }else if (activity & kCFRunLoopAfterWaiting) {
48         NSLog(@"从休眠中唤醒");
49     }else if (activity & kCFRunLoopExit) {
50         NSLog(@"退出runloop");
51     }
52 }
53 
54 /// 事件
55 - (void)handleEvent:(NSString *)str
56 {
57     NSLog(@"%@", str);
58 }
59 
60 /// 添加source
61 - (void)addSourceEvent
62 {
63     CFRunLoopSourceContext context = {0, (__bridge void *)self, NULL, NULL, NULL, NULL, NULL, &mySchedule, &myCancel, &myPerform};
64     CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
65     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
66     /// 将source将标记为待处理
67     CFRunLoopSourceSignal(source);
68     /// 唤醒runloop
69     CFRunLoopWakeUp(CFRunLoopGetCurrent());
70     
71 }
72 
73 /// 添加输入源时的回调
74 void mySchedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode)
75 {
76     NSLog(@"输入源被添加");
77 }
78 
79 /// 移除输入源时的回调
80 void myCancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode)
81 {
82     NSLog(@"输入源被移除");
83 }
84 
85 /// 执行输入源时的回调
86 void myPerform(void *info)
87 {
88     NSLog(@"输入源正在被执行");
89 }

 参考: https://juejin.im/post/59a04e125188252445326e89

原文地址:https://www.cnblogs.com/jisa/p/7486528.html