iOS多线程应用(一):概述、NSThread

前言

本系列文章列表

为什么会写这系列文章?

现在网络上有着大量的关于多线程的文章,“深入剖析”、“底层原理”这些看似高大上的字眼很多,然而大部分文章完全没达到那个地步,仅仅是使用API的水平,关键是还有很多错误和不严谨的地方?(当然,也有极少的好文。

本系列文章主要讲解多线程的方方面面,目的只是为了让新手们提升或者让老鸟复习一下。行文尽量严谨,如果有错误跪谢大佬们指出,毕竟笔者水平有限。

本系列文章主要是应用层面的东西,一般不涉及底层。

一、多线程概述

惯例,来点基础知识和概念

  • 进程与线程:线程是进程内假想的持有CPU使用权的执行单位。一个程序启动便有一个进程,一个进程可以有多个线程,但只有一个主线程。
  • 父线程与子线程:创建线程时,创建方的线程为父线程,被创建方的线程为子线程。父线程可以等到子线程执行完毕后与其会和;也可以切断它们的关系让它们分离(比如NSThread)
  • 共享变量:多个线程都能访问的变量称为共享变量,因为地址空间是共享的,所以理论上所有的内存区域都是共享变量。如果不同线程同时访问同一个变量,这个变量就是不安全的,我们需要通过一些骚操作来实现线程间互斥(比如后面会讲的各种锁)
  • 并发与并行:并行一定并发,并发不一定并行。在单核设备上,CPU通过频繁的切换上下文来运行不同的线程,速度足够快以至于我们看起来它是‘并行’处理的,然而我们只能说这种情况是并发而非并行。例如:你和两个人一起百米赛跑,你一直在不停的切换跑道,而其他两人就在自己的跑道上,最终,你们三人同时到达了终点。我们把跑道看做任务,那么,其他两人就是并行执行任务的,而你只能的说是并发执行任务(嗯,这个例子还行)。
  • 多线程底层:多线程的底层实现机制是基于Mach的,Mach是第一个以多线程处理任务的系统,但是Mach级别的线程是相互独立的,线程之间不能通讯。

二、NSThread

NSThread是我们用来创建并管理线程的类,它的使用很简单,暴露的接口也很有限。通常情况下,我们更多的是使用GCD和NSOperation来管理任务,而不用去管理线程的布局,因为毕竟CPU和GCD已经做得很好了。

我有个大胆的想法——直接贴上常用的API(哈哈省事):

//获取当前线程,请注意,这里是类的属性
@property (class, readonly, strong) NSThread *currentThread;

//这两个方法是便利构造类方法,用它们创建的线程会立马执行不用start,上面个是ios10.0过后的方法,使用的小伙伴要注意了
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (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;
//设置当前线程的优先级
+ (BOOL)setThreadPriority:(double)p;

//线程优先级
@property double threadPriority API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); // To be deprecated; use qualityOfService below

//线程名字
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

//是否是主线程,一个是类方法,一个是实例方法
@property (readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (class, readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // reports whether current thread is main

//获取主线程
@property (class, readonly, strong) NSThread *mainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

//初始化方法,没什么可说的
- (instancetype)init API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

//正在执行
@property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//已经完成
@property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//已经取消
@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

//手动取消
- (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

//手动开始
- (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

//入口函数,如果你要重写thread就得override,当然,我这里没贴上的api重写thread应该能用到。不过重写这种骚操作基本不会用到。
- (void)main API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // thread body method

下面是延展里面的Api

//几个通知,看命名就知道意思了
FOUNDATION_EXPORT NSNotificationName const NSWillBecomeMultiThreadedNotification;
FOUNDATION_EXPORT NSNotificationName const NSDidBecomeSingleThreadedNotification;
FOUNDATION_EXPORT NSNotificationName const NSThreadWillExitNotification;

@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));

怎么样,是不是很简单,实际开发中,对于NSThread的使用还是比较多,主要就是获取当前线程currentThread,线程休眠sleepForTimeInterval:,判断是否在主线程isMainThread,以及回到主线程performSelector: onThread:系列方法等等。

本系列文章列表

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

Windows DNS API RCE漏洞分析及PoC构造

根据 Microsoft 2017 年 10 月安全通告,多个版本 Windows 中的 dnsapi.dll 在处理 DNS response 时可导致 SY...

36710
来自专栏数据库新发现

Statspack之十四-"log file sync" 等待事件

http://www.eygle.com/statspack/statspack14-LogFileSync.htm 当一个用户提交(commits)或者回滚...

1201
来自专栏deepcc

sublime text2之js压缩-Js Minifier

37513
来自专栏影子

SpringBoot自定义拦截器实现IP白名单功能

4.4K2
来自专栏老司机的简书

老司机出品———疯狂造轮子之图片异步下载类

SDWebImage,我估计没有一个做iOS的不知道这个三方库吧,他为我们提供了简洁的图片异步下载方式。在他为我一句api带来这么大便利的同时,你有没有想过他是...

1184
来自专栏JackieZheng

Spring实战——Profile

  看到Profile这个关键字,或许你从来没有正眼瞧过他,又或者脑海中有些模糊的印象,比如除了这里Springmvc中的Profile,maven中也有Pro...

3166
来自专栏GopherCoder

『No18: Go 实现世界杯后台管理系统』

趁着周末更新一期,上一期讲到 如何快速熟悉一个项目, 文章的最后讲到,最好的方法是借用相同的技术栈重新实现一个项目。

1861
来自专栏影子

SpringBoot自定义拦截器实现IP白名单功能

1.6K11
来自专栏一“技”之长

iOS中CoreData数据管理系列四——进行数据与页面的绑定

    在上一篇博客中,我们讨论了CoreData框架中添加与查询数据的操作,事实上,在大多数情况下,这些数据都是由一个UITableView表视图进行展示的,...

751
来自专栏Google Dart

Flutter 构建完整应用手册-联网 顶

从大多数应用程序获取互联网上的数据是必要的。 幸运的是,Dart和Flutter为这类工作提供了工具!

1322

扫码关注云+社区

领取腾讯云代金券