此篇文章我將用最短的篇幅列舉RunLoop在實(shí)際項(xiàng)目中幾種具體的用法碍庵,以便以后使用時(shí)查閱绍载。
應(yīng)用1:創(chuàng)建常駐線程
首先上經(jīng)典代碼:
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
?
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
類(lèi)似的,項(xiàng)目中薄辅,我們也可以在單例工具中實(shí)現(xiàn)一個(gè)健康長(zhǎng)壽的子線程了。
應(yīng)用2:優(yōu)化定時(shí)器NSTimer
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTask) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[timer fire];
上面代碼以下情形可以直接用:
我們都知道抠璃,可滑動(dòng)視圖在滑動(dòng)的時(shí)候站楚,runloop會(huì)切換到UITrackingRunLoopMode,這時(shí)NSDefaultRunLoopMode下的NSTimer是不會(huì)跑的搏嗡,在實(shí)際運(yùn)用中窿春,這可能會(huì)是一個(gè)問(wèn)題。
舉個(gè)具體的例子采盒,如今類(lèi)似釘釘簽到的功能非常常見(jiàn)旧乞,一般會(huì)有一個(gè)準(zhǔn)確的時(shí)間在屏幕上跑,這個(gè)時(shí)間需要準(zhǔn)確無(wú)誤(為了踩點(diǎn)8:59:59)的話磅氨,我們就要做到界面滑動(dòng)時(shí)timer依然在運(yùn)行尺栖。
應(yīng)用3:發(fā)現(xiàn)和消除卡頓
這個(gè)一看就說(shuō)來(lái)話長(zhǎng),首先是如何發(fā)現(xiàn)卡頓烦租,用到的是CFRunLoopObserverRef延赌,這家伙可以監(jiān)聽(tīng)RunLoop的各個(gè)狀態(tài),利用這一點(diǎn)可以檢測(cè)出RunLoop 在進(jìn)入睡眠之前和喚醒后的兩個(gè)狀態(tài)的耗時(shí)叉橱。推薦看看微信iOS卡頓監(jiān)控系統(tǒng)挫以。
而發(fā)現(xiàn)并不是最終目的,如何消除才是關(guān)鍵窃祝,最經(jīng)典的例子就是18圖分步加載這個(gè)了掐松。
應(yīng)用4:讓APP穢土轉(zhuǎn)生
是不是有越來(lái)越????的感覺(jué)?是的锌杀,RunLoop讓我們可以使用火影忍者中那個(gè)究極禁術(shù)“穢土轉(zhuǎn)生”讓我們得APP更強(qiáng)大甩栈。哈哈泻仙,其實(shí)是使用異常捕捉技術(shù)和RunLoop讓APP一些一般的崩潰能夠繼續(xù)往下運(yùn)行糕再,直接看具體代碼吧。
//
// BBCrashHandler.h
// RunloopDemo
//
// Created by xyb on 2019/8/2.
// Copyright ? 2019年 XYB. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BBCrashHandler : NSObject
+ (instancetype)sharedInstance;
@end
NS_ASSUME_NONNULL_END
//
// BBCrashHandler.m
// RunloopDemo
//
// Created by xyb on 2019/8/2.
// Copyright ? 2019年 XYB. All rights reserved.
//
#import "BBCrashHandler.h"
#import <UIKit/UIKit.h>
#include <libkern/OSAtomic.h>
#include <execinfo.h>
NSString * const kSignalExceptionName = @"kSignalExceptionName";
NSString * const kSignalKey = @"kSignalKey";
NSString * const kCaughtExceptionStackInfoKey = @"kCaughtExceptionStackInfoKey";
void HandleException(NSException *exception);
void SignalHandler(int signal);
@implementation BBCrashHandler
static BBCrashHandler *instance = nil;
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
});
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
- (instancetype)init
{
self = [super init];
if (self) {
[self setCatchExceptionHandler];
}
return self;
}
- (void)setCatchExceptionHandler
{
// 1.捕獲一些異常導(dǎo)致的崩潰
NSSetUncaughtExceptionHandler(&HandleException);
// 2.捕獲非異常情況玉转,通過(guò)signal傳遞出來(lái)的崩潰
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
}
+ (NSArray *)backtrace
{
void* callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
for (int i = 0; i < frames; i++) {
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backtrace;
}
- (void)handleException:(NSException *)exception
{
NSString *message = [NSString stringWithFormat:@"崩潰原因如下:\n%@\n%@",
[exception reason],
[[exception userInfo] objectForKey:kCaughtExceptionStackInfoKey]];
NSLog(@"%@",message);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"程序崩潰了"
message:@"如果你能讓程序起死回生突想,那你的決定是?"
delegate:self
cancelButtonTitle:@"崩吧"
otherButtonTitles:@"穢土轉(zhuǎn)生", nil];
[alert show];
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (1) {
for (NSString *mode in (__bridge NSArray *)allModes) {
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
}
CFRelease(allModes);
NSSetUncaughtExceptionHandler(NULL);
signal(SIGABRT, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
if ([[exception name] isEqual:kSignalExceptionName]) {
kill(getpid(), [[[exception userInfo] objectForKey:kSignalKey] intValue]);
} else {
[exception raise];
}
}
@end
void HandleException(NSException *exception)
{
// 異常堆棧信息
NSArray *callStack = [exception callStackSymbols];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:callStack forKey:kCaughtExceptionStackInfoKey];
BBCrashHandler *crashObject = [BBCrashHandler sharedInstance];
NSException *customException = [NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo];
[crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES];
}
void SignalHandler(int signal)
{
NSArray *callStack = [BBCrashHandler backtrace];
BBCrashHandler *crashObject = [BBCrashHandler sharedInstance];
NSException *customException = [NSException exceptionWithName:kSignalExceptionName
reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.", nil),signal]
userInfo:@{kSignalKey:[NSNumber numberWithInt:signal]}];
[crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES];
}
上面代碼的原理是使用異常捕捉回調(diào)獲取到崩潰的相關(guān)信息究抓,然后獲取當(dāng)前runloop猾担,并執(zhí)行所有Mode,不過(guò)只能有一次機(jī)會(huì)刺下,第二次就不好使了绑嘹。
本文完??。