(由于合在一起感觉一篇太长翻着累)
iOS多线程目前总结了四篇
- iOS基础深入补完计划--多线程(面试题)汇总
- iOS基础深入补完计划--NSThread
- iOS基础深入补完计划--GCD
- iOS基础深入补完计划--NSOperation
欢迎移步O(∩_∩)O
NSThread
NSThread是轻量级的多线程开发、使用起来也并不复杂、但是使用NSThread需要自己管理线程生命周期、实在不是特别优雅。
(个人)通常的用处分为以下几个地儿吧:
- 判断/获取当前线程
if (![[NSThread currentThread] isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
//主线程执行
});
}
- 休眠当前线程
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
- 给线程起个名字
[NSThread currentThread].name =@"我要标记这个线程"];
总之NSThread基本都是对线程本身进行操作才会用到。
NSThread相关的几个坑:
- [thread cancel]和[NSThread exit];
- 调用实例对象的
- (void)cancel;
方法、并不会直接取消线程。其作用只是对应线程的cancel属性设置为YES、线程依旧继续执行。在此之后:
thread.cancel
为YES。
thread.executing
为YES。
thread.finished
为NO。(除非全执行完了) - 我们需要在适当的位置执行
[NSThread exit];
才可以真正关闭线程、余下的代码不会继续执行。同时:
thread.cancel
为你设置的状态(默认应该是NO)。
thread.executing
为NO。
thread.finished
为YES。
- 调用实例对象的
- 一些NSObject的相关扩展方法(
performSelector
)
(主要)依赖Runtime的扩展
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
基本等于自己手动调用方法、但是其最基本原理是runtime、编译的时候也不会做任何校验。所有的performSelector
方法、都是基于runtime进行消息转发的。
(主要)依赖NSThread的扩展
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
举了两个例子、注意这里都只跟线程有关(OnMainThread
/InBackground
)。
(主要)依赖Runloop的扩展
- (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
第一个很明显了、依赖Runloop的NSRunLoopMode。
第二个表面上看没什么关系、但内部依赖定时器。同时、定时器是依赖Runloop的。
NSThred的API
@interface NSThread : NSObject {
@private
id _private;
uint8_t _bytes[44];
}
// 获取当前线程
+ (NSThread *)currentThread;
// 创建新线程
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
// 是否是多线程
+ (BOOL)isMultiThreaded;
/**
* 每个线程都维护了一个“键-值”的字典,它可以在线程里面的任何地方被访问,
* 可以使用该字典来保存一些信息,这些信息在整个线程的执行过程中都保持不变。
* 比如,可以使用它来存储在整个线程过程中RunLoop里面多次迭代的状态信息。
* 使用:通过threadDictionary方法获取一个NSMutableDictionary对象,然后添加需要的字段和数据
*/
@property (readonly, retain) NSMutableDictionary *threadDictionary;
// 设置线程睡眠/堵塞
+ (void)sleepUntilDate:(NSDate *)date;
// 设置线程睡眠/堵塞
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 结束/退出进程
+ (void)exit;
// 获取线程的优先级
+ (double)threadPriority;
// 设置线程优先级,取值范围0.0~1.0
+ (BOOL)setThreadPriority:(double)p;
// 线程优先级,iOS8以后推荐使用qualityOfService属性,通过量化的优先级枚举值来设置
@property double threadPriority;
/** 线程优先级
qualityOfService的枚举值如下:
NSQualityOfServiceUserInteractive:最高优先级,用于用户交互事件
NSQualityOfServiceUserInitiated:次高优先级,用于用户需要马上执行的事件
NSQualityOfServiceDefault:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
NSQualityOfServiceUtility:普通优先级,用于普通任务
NSQualityOfServiceBackground:最低优先级,用于不重要的任务
*/
@property NSQualityOfService qualityOfService;
// 返回当前线程在栈中所占的地址所组成的数组
+ (NSArray *)callStackReturnAddresses NS_AVAILABLE(10_5, 2_0);
// 返回栈空间的符号表
+ (NSArray *)callStackSymbols NS_AVAILABLE(10_6, 4_0);
// 线程名称
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
// 栈的所占空间大小
@property NSUInteger stackSize NS_AVAILABLE(10_5, 2_0);
// 是否是主线程
@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);
// 初始化线程
- (instancetype)init NS_AVAILABLE(10_5, 2_0) NS_DESIGNATED_INITIALIZER;
// 初始化线程
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
// 是否正在执行
@property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);
// 是否执行完毕
@property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);
// 是否已经取消/中止
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);
// 取消线程,不能再开始
- (void)cancel NS_AVAILABLE(10_5, 2_0);
// 开始线程
- (void)start NS_AVAILABLE(10_5, 2_0);
/** main是线程入口
* - (void)main的使用:
* 1. 一般创建线程会子类化NSThread,重写main方法,把关于线程执行的方法都写在里面,这样可以在任何需要这个线程方法的地方直接使用。
* 2. 把线程执行的方法写在main里,是因为线程的操作应该属于线程的本身,而不是每次使用都通过initWithTarget:selector:object:方法,且再一次实现某个方法。
* 3. 当重写了main方法后,同时使用initWithTarget:selector:object:方法初始化,调用某个方法执行任务,系统默认只执行main方法里面的任务。
* 4. 如果直接使用NSThread创建线程,线程内执行的方法都是在当前的类文件里面的。
*/
- (void)main NS_AVAILABLE(10_5, 2_0);
@end
最后
本文主要是自己的学习与总结。如果文内存在纰漏、万望留言斧正。如果不吝赐教小弟更加感谢。