iOS多线程技术1 - NSThread的一般用法

NSThread 简介

NSThread 是苹果官方提供的面向对象类线程操作技术,简单方便,可以直接操作线程对象,不过需要自己控制线程的生命周期,在平时使用较少。

开启线程

使用 NSThread 开启线程有类方法和实例方法两种,类方法会直接执行任务,实例方法需要在实例初始化后调用 start 方法才能开始执行任务。

类方法创建线程

使用类方法创建线程后会自动开始任务,不需要手动开启。类方法创建线程有如下两种方式:

// block 方式
+ (void)detachNewThreadWithBlock:(void (^)(void))block;
     
// SEL 方式
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

block 方式:

[NSThread detachNewThreadWithBlock:^{
    NSLog(@"class method -- task in block, current thread is %@", [NSThread currentThread]);
}];

// 执行后打印:class method -- task in block, current thread is {number = 4, name = (null)}

SEL 方式:

[NSThread detachNewThreadSelector:@selector(classMethodTest) toTarget:self withObject:nil];

- (void)classMethodTest {
    NSLog(@"class method -- task in SEL, current thread is %@", [NSThread currentThread]);
}

// 执行后打印:class method -- task in SEL, current thread is {number = 3, name = (null)}

实例方法创建线程

实例方法创建线程也是分为 block 和 SEL 两种方式:

// block 方式
- (instancetype)initWithBlock:(void (^)(void))block;
     
// SEL 方式
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;

block 方式:

// 1. 创建线程
NSThread *oneThread = [[NSThread alloc] initWithBlock:^{
    NSLog(@"instance method -- task in block, current thread is %@", [NSThread currentThread]);
}];
// 2. 开始执行
[oneThread start];

// 执行后打印:instance method -- task in block, current thread is {number = 5, name = (null)}

SEL 方式:

// 1. 创建线程
NSThread *anotherThread = [[NSThread alloc] initWithTarget:self selector:@selector(instanceMethodTest) object:nil];
// 2. 开始执行
[anotherThread start];

- (void)instanceMethodTest {
    NSLog(@"instance method -- task in SEL, current thread is %@", [NSThread currentThread]);
}

// 执行后打印:instance method -- task in SEL, current thread is {number = 6, name = (null)}

NSThread 属性

下面,来看一下 NSThread 中都有哪些属性:

当前线程(类属性)

@property (class, readonly, strong) NSThread *currentThread;

线程字典

每个线程都维护了一个 键-值 字典,它可以在线程里面的任何地方被访问。可以使用该字典来保存一些信息,这些信息在整个线程的执行过程中都保持不变。

@property (readonly, retain) NSMutableDictionary *threadDictionary;

NSThread *myThread = [[NSThread alloc] initWithBlock:^{
    NSLog(@"the threadDictionary of myThread is %@", [NSThread currentThread].threadDictionary);
}];

[myThread.threadDictionary setValue:@"value1" forKey:@"key1"];
[myThread.threadDictionary setValue:@"value2" forKey:@"key2"];

[myThread start];

// 执行后打印:
/*
the threadDictionary of myThread is {
    key1 = value1;
    key2 = value2;
}
*/

线程优先级

@property double threadPriority; // 已废弃,官方建议使用下方的 qualityOfService 属性
@property NSQualityOfService qualityOfService; 
// 注意:线程启动后该属性只读

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21,
    NSQualityOfServiceUserInitiated = 0x19,
    NSQualityOfServiceUtility = 0x11,
    NSQualityOfServiceBackground = 0x09,
    NSQualityOfServiceDefault = -1
};

线程名称

@property (nullable, copy) NSString *name;

线程使用栈区大小(默认是 512K)

@property NSUInteger stackSize;

线程是否是主线程

@property (readonly) BOOL isMainThread;

NSLog(@"myThread is%@ main thread.", myThread.isMainThread ? @"" : @" not");
// 打印:myThread is not main thread.

线程是否正在执行

@property (readonly, getter=isExecuting) BOOL executing;

线程是否执行完毕 注意:线程取消之后 isFinished 的值也是 YES

@property (readonly, getter=isFinished) BOOL finished;

线程是否已经取消

@property (readonly, getter=isCancelled) BOOL cancelled;

当前线程是否是主线程(类属性)

@property (class, readonly) BOOL isMainThread; // reports whether current thread is main

NSLog(@"current thread is%@ main thread.", NSThread.isMainThread ? @"" : @" not");
// 在主线程中打印:current thread is main thread.

获取主线程(类属性)

@property (class, readonly, strong) NSThread *mainThread;

NSLog(@"the main thread is %@", NSThread.mainThread);
// 打印:the main thread is {number = 1, name = main}

栈返回地址记录(类属性)

线程调用就会有函数的调用,函数的调用就会有栈返回地址的记录,在这里读取的是函 数调用返回的虚拟地址,也就是在该线程中函数调用的虚拟地址的数组。

@property (class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses;

栈返回标志记录(类属性)

和上面的属性类似,只不过读取的是该线程调用函数的名字的数组。

@property (class, readonly, copy) NSArray<NSString *> *callStackSymbols;

注意:callStackReturnAddress 和 callStackSymbols 这两个只读属性可以同 NSLog 函数联合使用用来跟踪线程的函数调用情况,是编程调试的重要手段。

NSThread 实例方法

初始化方法:

- (instancetype)init;

- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;

- (instancetype)initWithBlock:(void (^)(void))block;

启动线程

- (void)start;

取消线程

- (void)cancel;

线程的入口函数

- (void)main;

NSThread 类方法

开启新线程(自动执行任务)

+ (void)detachNewThreadWithBlock:(void (^)(void))block;
     
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

当前线程是否是子线程

+ (BOOL)isMultiThreaded;

线程休眠

// 休眠至指定日期
+ (void)sleepUntilDate:(NSDate *)date;

// 休眠指定时间
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

退出当前线程

+ (void)exit;

获取当前线程的优先级

+ (double)threadPriority;

设置当前线程的优先级(优先级的取值范围是 0.0 ~ 1.0,默认是 0.5,值越大,优先级越高。)

+ (BOOL)setThreadPriority:(double)p;

线程的隐式创建 & 线程间的通讯

在 NSObject 的 NSThreadPerformAdditions 分类中,包含如下方法,也就是说所有继承 NSObject 的类的实例对象都可调用以下方法:

在主线程中执行指定的方法

参数1:SEL 方法
参数2:方法参数
参数3:是否等待当前任务执行完毕
参数4:指定 Runloop model

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
     
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes

在某个线程中执行指定的方法

参数1:SEL 方法
参数2:方法参数
参数3:是否等待当前任务执行完毕
参数4:指定的Runloop model

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
     
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes

在开启的子线程中执行指定的方法

参数1:SEL 方法
参数2:方法参数

- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
好用请点 ???

你可能感兴趣的:(iOS深入学习,NSThread)