线程:一个独立执行代码的路径
进程:一个可执行程序,包含多个线程
将与UI界面显示、影响界面流畅度的事情 都应该 子线程处理。
带有返回值的创建
// 创建子线程 1
NSThread *oneThread = [[NSThread alloc]initWithTarget:self selector:@selector(threadEvent) object:nil];
注意:此种方式创建的线程需要手动启动,下面会讲到。
不带返回值,创建直接启动进入线程
[NSThread detachNewThreadSelector:@selector(threadEvent3:) toTarget:self withObject:@"123"];
// 进入线程 1
[oneThread start];
// 进入线程 2
[twoThread start];
+ (void)sleepUntilDate:(NSDate *)date;
//[NSThread sleepForTimeInterval:1.0];暂停一秒
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];暂停一秒
对于线程的取消,NSThread提供了一个取消的方法和一个属性
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);
- (void)cancel NS_AVAILABLE(10_5, 2_0);
注意:调用-cancel方法并不会立刻取消线程,它仅仅是将cancelled属性设置为YES。cancelled也仅仅是一个用于记录状态的属性。线程取消的功能需要我们在main函数中自己实现.比如上面在创建线程的时候指定的方法threadEvent
就是这个线程的man函数。
-exit函数可以让线程立即退出,不管线程是否执行完毕,如果任务没有执行完,内存泄漏。
+ (void)exit;
例如:主线程拿到json数据,丢给子线程,子线程解析完毕,再传回主线程。
//①
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
//②
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//③
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array NS_AVAILABLE(10_5, 2_0);
//④
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
希望先执行的线程设定高优先级,不着急的低优先级
+ (double)threadPriority;//设置和获取当前线程的优先级
+ (BOOL)setThreadPriority:(double)p;//设置和获取当前线程的优先级
@property double threadPriority NS_AVAILABLE(10_6, 4_0); // To be deprecated; use qualityOfService below 通过对象设置和获取优先级
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0); // read-only after the thread is started 优先级的枚举属性
枚举优先级
typedef NS_ENUM(NSInteger, NSQualityOfService) {
NSQualityOfServiceUserInteractive = 0x21,//最高 用户交互级别
NSQualityOfServiceUserInitiated = 0x19,//次高
NSQualityOfServiceDefault = -1//默认
NSQualityOfServiceUtility = 0x11,//不需要立即返回的任务
NSQualityOfServiceBackground = 0x09,//后台 一点都不着急的
}
@property (readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);
+ (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main 判断当前线程是否是主线程
+ (NSThread *)mainThread NS_AVAILABLE(10_5, 2_0);//获取主线程的thread
在线程的main函数中获取当前线程
+ (NSThread *)currentThread;
注册线程通知相关
//由当前线程派生出第一个其他线程时发送,一般一个线程只发送一次
NSString * const NSWillBecomeMultiThreadedNotification;
//我没用过。。。。。看意思是成为单线程通知
NSString * const NSDidBecomeSingleThreadedNotification;
//线程退出
NSString * const NSThreadWillExitNotification;
每一个线程都有一个runloop,如果不希望线程执行完任务退出,而是等待继续执行。需要启动runloop,主线程的runloop是系统自动启动的。
runloop的特点
RunLoop是iOS事件响应与任务处理最核心的机制
主线程的RunLoop在应用启动的时候就会自动创建
什么时候使用RunLoop
1,通知Observer,即将进入loop
2,通知Observer,将要处理timer
3,通知Observer,将要处理Source0
4,处理Source0
5,如果有Source1,跳到第9步
6,通知Obesrcer,线程即将休眠
7,休眠,等待唤醒
8,通知Observer,线程刚被唤醒
9,处理唤醒时收到的消息,之后跳回2
10,通知Oberver,即将退出Loop
在 CoreFoundation 里面关于 RunLoop 有5个类:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。
CFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。
• Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
• Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。
CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。
CFRunLoopObserverRef 是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
};
上面的 Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。