iOS 多线程(三)NSThread

优缺点

1.优点:NSThread比其他两种多线程方案较轻量级,更直观地控制线程对象
2.缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销

一、线程的创建

// 1. 初始化线程
// selector :线程执行的方法,这个selector最多只能接收一个参数
// target :selector消息发送的对象
// argument : 传给selector的唯一参数,也可以是nil
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;

// 2. 创建并开启新线程
- (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

// 3. 隐式创建并开启线程
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;

// 4.获得主线程
+ (NSThread *)mainThread;   

// 5.获取当前线程
+ (NSThread *)currentThread;
复制代码

线程状态

线程状态的变化参考多线程(一)进程与线程

// 启动线程
// 线程进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
- (void)start;

// 阻塞(暂停)线程方法
// 线程进入阻塞状态
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

// 强制停止线程
// 线程进入死亡状态
+ (void)exit;
复制代码
// 暂停2s
[NSThread sleepForTimeInterval:2];

// 或者
NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]];
[NSThread sleepUntilDate:date];
复制代码

其他方法

// 判断是否为主线程(对象方法)
- (BOOL)isMainThread;

// 判断是否为主线程(类方法)
+ (BOOL)isMainThread;    
	
// 线程的名字——setter方法
- (void)setName:(NSString *)n;    

// 设置线程的优先级(0.0 - 1.0,1.0最高级)
- (void)setThreadPriority:(double)p;
复制代码

线程间的通信

// 1. 在主线程上执行操作
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array;
  // equivalent to the first method with kCFRunLoopCommonModes

// 2. 在指定线程上执行操作
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

// 3. 在当前线程上执行操作,调用 NSObject 的 performSelector:相关方法
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
复制代码

下面是一个下载图片的实例:

  1. 在子线程下载图片;
  2. 下载图片完成之后,回调到主线程刷新视图。
/**
 * 创建一个线程下载图片
 */
- (void)downloadImageOnSubThread {
    // 在创建的子线程中调用downloadImage下载图片
    [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}

/**
 * 下载图片,下载完之后回到主线程进行 UI 刷新
 */
- (void)downloadImage {
    
    NSLog(@"current thread -- %@", [NSThread currentThread]);
    
    // 1. 获取图片 imageUrl
    NSURL *imageUrl = [NSURL URLWithString:@"https://wiki-1259056568.cos.ap-shanghai.myqcloud.com/ios/20190627101912.png"];
    
    // 2. 从 imageUrl 中读取数据(下载图片) -- 耗时操作
    NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
    // 通过二进制 data 创建 image
    UIImage *image = [UIImage imageWithData:imageData];
    
    // 3. 回到主线程进行图片赋值和界面刷新
    [self performSelectorOnMainThread:@selector(refreshOnMainThread:) withObject:image waitUntilDone:YES];
}

/**
 * 回到主线程进行图片赋值和界面刷新
 */
- (void)refreshOnMainThread:(UIImage *)image {
    NSLog(@"current thread -- %@", [NSThread currentThread]);
    
    // 赋值图片到imageview
    self.imageView.image = image;
}
复制代码

NSThread 子类

You can subclass NSThread and override the [main](https://developer.apple.com/documentation/foundation/nsthread/1418421-main) method to implement your thread’s main entry point. If you override [main](https://developer.apple.com/documentation/foundation/nsthread/1418421-main), you do not need to invoke the inherited behavior by calling super.

参考

链接

  1. NSThread Reference
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享