IOS多线程二NSThread简约而不简单
今天就来着手教大家在IOS中简单的实现多线程。IOS实现多线程的方式有几种,但是要说最容易上手的还是NSThread,在IOS实现多线程的几种方法中,在我看来其实没有明显的好坏之分,只有适合之说,看何种场景下适合使用何种方式。
NSThread相对来说更加轻量级,并且更加具体,没有那么抽象,一个线程就是一个类的对象。你可以给这个对象附上需要执行的内容,其实也就是一个方法,可以通过调用该类的相关方法,控制其启动、关闭、暂停等。好吧,这里就不多说了,下面着重看下NSThread这个类。
直接在Xcode中就可以进入到NSThread类的.h文件中。可以看到NSThread是一个NSObject类的子类,是IOS开发的一个重要子框架Foundation框架中的一个类。进入NSThread类的.h文件可以看到一些NSThread比较常见的方法和属性。如下所述:
1.NSThread的init方法。
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullableid)argumentNS_AVAILABLE(10_5,2_0);
该方法主要是用一个目标对象的方法来创建一个多线程NSThread类。其中target参数传递目标对象,selector参数传递目标对象的一个方法,其实就是多线程需要执行的内容。argument可以传递一个临时参数到多线程执行的方法中。
2.Start方法
该方法是控制线程启动执行的方法。init方法创建的NSThread对象,只有调用该方法才能启动执行。
3.detachNewThreadSelector方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullableid)argument;
该方法与init方法类似,参数也是一样,区别就在于,init方法初始化好一个多线程对象之后,需要调用start方法启动执行,而该方法创建好后直接自己启动执行,不需要再调用start方法。并且也不会产生显示的对象,无法通过对象的方法来控制结束或者暂停执行线程。
4.类方法isMultiThreaded
+ (BOOL)isMultiThreaded;
判断当前代表执行是否在主线程中
5.类方法currentThread
+ (NSThread*)currentThread;
获取当前线程。
6.线程暂停方法
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
该方法可传递一个秒级别的参数ti,来暂停执行当前代码所在的线程ti秒。
7.退出线程方法
+ (void)exit;
该方法是真正可以让线程退出的方法,在某个线程中调用该方法,该线程就会直接退出,之后的代码就不会再执行,而且要特别指出,该方法如果直接在主线程中调用的话,连主线程都会被直接终止哦,主线程如果都被终止了,那等于整个程序就已经失去活力了,APP都会整个卡死。
8 cancle标志方法
- (void)cancelNS_AVAILABLE(10_5,2_0);
@property(readonly,getter=isCancelled)BOOLcancelledNS_AVAILABLE(10_5,2_0);
很多初学者以为cancel就是退出线程,其实不是,这里的cancel其实就是类中的一个BOOL型变量,用户调用了cancel方法,该BOOL型变量就变成了真,再调用isCancel就可以看到其值是YES还是NO,然后再根据该标志位做一些工作。所以对比一下退出线程还是要靠exit方法,或者直接在线程中判断调用return退出。
9 设置线程优先级
+ (BOOL)setThreadPriority:(double)p;
@propertydoublethreadPriorityNS_AVAILABLE(10_6,4_0);// To be deprecated; use qualityOfService below
通过以上方法可以查看和设置线程优先级,优先级高的线程会优先执行,这里不建议进行此类设置,会打乱系统之前的线程优先级安排。可能造成某些底等级的线程卡顿或者长时间得不到执行。
以上大致就是NSThread类已经类中相关的属性和方法的基本介绍。下面就讲3个具体使用NSThread实现多线程的例子。
1.启动一个线程
代码示例
- (void)viewDidLoad
{
[super viewDidLoad];//进入某个viewController时启动一个线程
//使用已经写好的线程执行方法FunctionForNSThread初始化一个NSThread类对象
NSThread* nowTestThread = [[NSThread alloc] initWithTarget:self selector:@selector(FunctionForNSThread) object:nil];
//启动该线程类对象
[nowTestThread start];
}
- (void)FunctionForNSThread
{
if([NSThread isMainThread])//使用NSThread类方法isMainThread判断当前线程是否为主线程
{//主线程输出信息
NSLog(@"now is in MainThread");
}
else
{//非主线程输出信息
NSLog(@"now is not in MainThread");
}
}
这里的输出结果为
2016-09-20 23:12:25.566 MultiThreadExample[622:15261] now is not in MainThread
说明我们已经启动了另外一个线程。
2.启动一个循环执行的线程。
- (void)viewDidLoad
{
[super viewDidLoad];//进入某个viewController时启动一个线程
//使用已经写好的线程执行方法FunctionForNSThread初始化一个NSThread类对象
NSThread* nowTestThread = [[NSThread alloc] initWithTarget:self selector:@selector(FunctionForNSThread) object:nil];
//启动该线程类对象
[nowTestThread start];
}
- (void)FunctionForNSThread
{
intnowIndex =0;
while(YES)//while循环执行
{
nowIndex++;
[NSThread sleepForTimeInterval:1];//每次执行到这里让线程暂停1秒钟
if([NSThread isMainThread])//使用NSThread类方法isMainThread判断当前线程是否为主线程
{//主线程输出信息
NSLog(@"%d now is in MainThread",nowIndex);
}
else
{//非主线程输出信息
NSLog(@"%d now is not in MainThread",nowIndex);
}
}
}
该段代码输出结果如下:
2016-09-20 23:15:02.010 MultiThreadExample[634:16310] 1 now is not in MainThread
2016-09-20 23:15:03.016 MultiThreadExample[634:16310] 2 now is not in MainThread
2016-09-20 23:15:04.018 MultiThreadExample[634:16310] 3 now is not in MainThread
2016-09-20 23:15:05.021 MultiThreadExample[634:16310] 4 now is not in MainThread
2016-09-20 23:15:06.022 MultiThreadExample[634:16310] 5 now is not in MainThread
2016-09-20 23:15:07.023 MultiThreadExample[634:16310] 6 now is not in MainThread
2016-09-20 23:15:08.024 MultiThreadExample[634:16310] 7 now is not in MainThread
2016-09-20 23:15:09.025 MultiThreadExample[634:16310] 8 now is not in MainThread
2016-09-20 23:15:10.026 MultiThreadExample[634:16310] 9 now is not in MainThread
………
如果不强行终止程序,该线程会一直执行下去,数字编码也会从1开始一直不断增大。
还有一点我们在线程中使用[NSThreadsleepForTimeInterval:1];方法每次循环开始时让线程暂停1秒钟,大家一定要注意,在这种循环执行的线程中,一定要设置循环停止时间,否则线程不断死循环就会将设备CPU彻底占用,如果你运行的设备是双核CPU,你启动一个不设置循环停止时间的线程,就会直接将其中一个CPU全部沾满卡死。
从上述输出结果中也可以看到,每次输出间隔时间都是1秒钟。也就是说该循环执行的内容每隔1秒执行一次。
3.终止一个线程
停止一个线程有很多方法,初学者常犯的一个错误就是调用NSThread类的cancel方法。时机调用该方法并没有作用,该方法中设置的cancel只是一个标记位,调用cancel方法之后,再通过iscancel方法获取该标记位就可以得到是否已经cancel,真正的cancel需要在该线程的执行方法体中调用类方法exit来真正退出该线程。
- (void)viewDidLoad
{
[super viewDidLoad];//进入某个viewController时启动一个线程
//使用已经写好的线程执行方法FunctionForNSThread初始化一个NSThread类对象
NSThread* nowTestThread = [[NSThread alloc] initWithTarget:self selector:@selector(FunctionForNSThread) object:nil];
//启动该线程类对象
[nowTestThread start];
[NSThread sleepForTimeInterval:10];//主线程暂停执行10秒钟,这里测试需要,一般不建议暂停主线程。
[nowTestThread cancel];
}
- (void)FunctionForNSThread
{
intnowIndex =0;
while(YES)//while循环执行
{
nowIndex++;
[NSThread sleepForTimeInterval:1];//每次执行到这里让线程暂停1秒钟
if([NSThread isMainThread])//使用NSThread类方法isMainThread判断当前线程是否为主线程
{//主线程输出信息
NSLog(@"%d now is in MainThread",nowIndex);
}
else
{//非主线程输出信息
NSLog(@"%d now is not in MainThread",nowIndex);
}
if([[NSThread currentThread] isCancelled])//先通过currentThread方法获取当前线程,再通过isCancelled方法获取cancel标志位
{
NSLog(@"退出线程之前");
[NSThread exit];//cancel标志位为真,说明线程外部已经发出了终止线程的标志信号,在本线程调用exit停止线程。
NSLog(@"退出线程之后");//该log永远不会被打印,因为在exit方法之后的代码都不会再执行
}
}
}
该段代码执行结果如下:
2016-09-20 23:28:37.258 MultiThreadExample[675:21235] 1 now is not in MainThread
2016-09-20 23:28:38.265 MultiThreadExample[675:21235] 2 now is not in MainThread
2016-09-20 23:28:39.267 MultiThreadExample[675:21235] 3 now is not in MainThread
2016-09-20 23:28:40.272 MultiThreadExample[675:21235] 4 now is not in MainThread
2016-09-20 23:28:41.277 MultiThreadExample[675:21235] 5 now is not in MainThread
2016-09-20 23:28:42.281 MultiThreadExample[675:21235] 6 now is not in MainThread
2016-09-20 23:28:43.283 MultiThreadExample[675:21235] 7 now is not in MainThread
2016-09-20 23:28:44.288 MultiThreadExample[675:21235] 8 now is not in MainThread
2016-09-20 23:28:45.294 MultiThreadExample[675:21235] 9 now is not in MainThread
2016-09-20 23:28:46.312 MultiThreadExample[675:21235] 10 now is not in MainThread
2016-09-20 23:28:46.312 MultiThreadExample[675:21235]退出线程之前
因为主线程在暂停10秒之后将之前启动的线程cancel标志置为YES,而在线程内部每次循环都会判断cancel标志位,如果为真就调用exit退出线程,而每次循环间隔为1秒,所以执行10次之后线程就退出了。
另外exit之后的代码都不会被执行,所以“退出线程之后”log没有打印。
以上就是IOS多线程实现方案之一 NSThread的相关内容,如果大家有什么心得或者不同见解可以找我沟通,QQ 1828141617 邮箱 yimaoruanjian@126.com