美文网首页初见
iOS:多线程(二) —— NSThread

iOS:多线程(二) —— NSThread

作者: G_Jayson | 来源:发表于2019-07-19 14:04 被阅读0次

    前言

    在日常开发中,多线程的使用能帮助我们解决很多问题,比如大量数据的运算,复杂程序的执行,以及利用锁来实现一些需求,本系列文章主要介绍 iOS 中多线程实现技术的用法。

    iOS:多线程(一) —— pthread

    关于 pthread 的介绍和使用请查看之前的文章,本篇文章针对 NSThread 来赘述。

    关于 NSThread

    NSThread 是苹果官方提供给我们的一种面向对象的轻量级多线程解决方案,一个 NSThread 对象代表一个线程,需要程序员手动管理线程的生命周期,处理线程同步等问题。

    NSThread 使用

    常用方法

    在日常开发中,我们经常会用 [NSThread currentThread] 来获取当前线程,便于开发调试,这是最常用的一个方法,除此之外,下面的这几个方法,使用频率也是非常高,基于 NSObjectNSThreadPerformAdditions 分类中的方法,继承自 NSObject 的子类都可以很方便的调用。

        // 当前线程睡到指定时间
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
    
        // 线程沉睡时间间隔 常用在设置启动页间隔
        [NSThread sleepForTimeInterval:1.0];
    
        // 返回调用堆栈信息 可用于调试
        
        // [NSThread callStackSymbols]  return NSArray
        // [NSThread callStackReturnAddresses]   return NSArray
        NSLog(@"callStackSymbols : %@", [NSThread callStackSymbols]);
        NSLog(@"callStackReturnAddresses : %@", [NSThread callStackReturnAddresses]);
    
    
    @interface NSObject (NSThreadPerformAdditions)
    
    // 指定方法在主线程中执行
    - (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
    
    // 指定方法在某个线程中执行
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
        // equivalent to the first method with kCFRunLoopCommonModes
    
    //  指定方法在开启的子线程中执行
    - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    
    @end
    
    创建线程
    • 实例方法创建线程
      实例方法创建线程,可以根据需要设置参数,调用 - start() 才能开启线程,调用 - cancel() 取消线程
        // SEL
        NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(nsThreadMethod:) object:@"thread"];
    
        NSThread *thread2 = [[NSThread alloc] init];
    
        // block 方式
        NSThread *thread3 = [[NSThread alloc] initWithBlock:^{
            NSLog(@"block 创建 thread  %s : %@", __func__, [NSThread currentThread]);
        }];
    
        // 设置名称
        thread1.name = @"thread1";
    
        // 设置线程优先级 调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高。
        thread3.threadPriority = 0.0;
    
        // 启动线程
        [thread1 start];
    
        // 线程是否正在执行
        if ([thread3 isExecuting]) {
            NSLog(@"thread1 is executing! ");
        }
        
        // 取消线程
        [thread1 cancel];
    
        // 线程是否撤销
        if ([thread1 isCancelled]) {
            NSLog(@"thread1 canceled!");
        }
    
        // 线程是否执行结束
        if ([thread3 isFinished]) {
            NSLog(@"thread3 is finished!");
        }
    
    • 类方法创建线程
      类方法创建 NSThread 不需要再调用 start 方法,设置参数是通过类方法设置
        // block 方式
        [NSThread detachNewThreadWithBlock:^{
            NSLog(@"类方法 block 创建 thread : %s : %@", __func__, [NSThread currentThread]);
        }];
    
        // SEL 方式
        [NSThread detachNewThreadSelector:@selector(nsThreadMethod:) toTarget:self withObject:nil];
        
        /*
         [NSThread currentThread];  获取当前线程
         [NSThread isMultiThreaded];  当前代码运行线程是否为子线程
         */
        
        // 当前线程睡到指定时间
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
    
        // 线程沉睡时间间隔 常用在设置启动页间隔
        [NSThread sleepForTimeInterval:1.0];
    
        // 获取线程优先级 / 设置优先级
        double priority = [NSThread threadPriority];
        NSLog(@"当前线程优先级 : %f", priority);
        [NSThread setThreadPriority:0.9];
    
    线程间通信

    通常,例如我们有个网络请求是在子线程中执行,请求成功后我么要回到主线程中刷新UI,这是时候我们就需要了解子线程和主线程之间的通信,NSThread 为我们提供了解决方案,调用 NSObjectNSObject (NSThreadPerformAdditions) 分类中的方法,所有继承自 NSObject 实例化对象都可调用以下方法

        // 指定方法在主线程中执行
        [self performSelectorOnMainThread:@selector(performMethod:)  // 要执行的方法
                               withObject:nil                         // 执行方法时,要传入的参数 类型为 id
                            waitUntilDone:YES];                       // 当前线程是否要被阻塞,直到主线程将我们指定的代码块执行完,当前线程为主线程,设置为YES时,会立即执行,为NO时加入到RunLoop中在下一次运行循环时执行
    
        [self performSelectorOnMainThread:@selector(performMethod:)
                               withObject:nil
                            waitUntilDone:YES
                                    modes:@[@"kCFRunLoopDefaultMode"]];
    
        // 指定方法在某个线程中执行
        - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
        - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    
        // 在当前线程上执行操作,调用 NSObject 的 performSelector:相关方法
        - (id)performSelector:(SEL)aSelector;
        - (id)performSelector:(SEL)aSelector withObject:(id)object;
        - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
        
      
        //  指定方法在开启的子线程中执行 (相当于创建了一个子线程,并且执行方法)
        [self performSelectorInBackground:@selector(performMethod:) withObject:nil];
    

    举个例子,我们来模拟网络请求成功回到线程刷新 UI 的实现

        // 开辟子线程模拟网络请求
       [NSThread detachNewThreadWithBlock:^{
            NSLog(@"类方法 block 创建 thread : %s : %@", __func__, [NSThread currentThread]);
            
            // 模拟网络请求耗时操作
            for (int i = 0; i < 2; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"网络请求中  %@",[NSThread currentThread]);
            }
            
            NSLog(@"网络请求成功 准备回到主线程刷新 UI  %@",[NSThread currentThread]);
            
            // 主线程刷新UI
            [self performSelectorOnMainThread:@selector(mainThreadRefreshUI) withObject:nil waitUntilDone:YES];
        }];
    
    // 主线程刷新 UI 调用方法
    - (void)mainThreadRefreshUI {
        NSLog(@"回到了主线程并且刷新 UI  %s : %@", __func__, [NSThread currentThread]);
    }
    

    跟我们预想的一样,网络请求耗时操作是在子线程中执行,执行结束后调用线程间通信方法回到了主线程刷新 UI。

    以上是关于 NSThread 的介绍和简单使用的说明,相关 demo 请参考
    https://github.com/G-Jayson/Multi-thread

    相关文章

      网友评论

        本文标题:iOS:多线程(二) —— NSThread

        本文链接:https://www.haomeiwen.com/subject/gulrlctx.html