下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

关于iOS多线程-NSThread编程全面剖析

作者:课课家教育     来源: http://www.kokojia.com点击数:1099发布时间: 2017-07-26 10:00:32

标签: 移动开发编程数据

  欢迎各位阅读本篇文章,1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行本篇文章讲述了iOS多线程-NSThread编程解析,课课家教育平台提醒大家:文章中有许多的小细节,因此大家一定要认真阅读本篇文章哦!

    再iOS开发中存在三种比较常用的实现多线程编程的方法,NSThread,NSOperation,GCD,今天的先来说NSThread,要想实现多线程编程就要弄清楚进程,线程,同步和异步等概念

  一.进程与线程

  以下是一些运行的扯谈,言语不够学术,还请见谅:

  1.进程就是当你点击应用程序的图标时,系统会为你创建一个用于运行该程序的进程,你的应用程序在神通广大也无法脱离操作系统的手掌心,在计算机世界里任何都遵循标准,有自描述,应用程序之间,操作系统之间应用程序之间都通过标准来交流,没有标准的计算机将寸步难行,进程是操作系统分配给你的运行环境,应用程序可以通过进程和操作系统交互(系统调用,反过来操作系统可以控制进程的生命周期和计算机硬件资源分配);

  线程则是进程中的一条执行路径,它拥有自己的运行栈状态机制,可以获得CPU运行时间片段。一个进程中可以有多个的线程,从而就会产生同步和异步的工作机制。(从编程的角度只要知道其运行原理并根据自己的知识储备抽象成自己的思维编程模式,最后都是为coding服务,至于实现细节原理应该是系统设计者所考虑的问题,所以各个人有不同的理解):

  2.操作系统在没有启动的时候,它并没有在内存中,它就是静静地躺在磁盘上,当你按下电源键时,先运行的是内嵌到硬件上的BIOS,是它把操作系统搬到内存中(至于它怎么搬的这是硬件厂商和操作系统厂商或者是协议好的。

  作为软件编程层面,只要了解是那么回事就行)再把计算机的使用权力交给操作系统,操作系统就负责管理分配计算机资源,而用户要想使用计算机硬件资源就要需要通过操作系统这个大管家,主要讲的就是操作系统的实现,通过它你能了解到计算机底层的运行原理,我们不需要熟读只要了解大意提升自己的编程思维就行。

关于iOS多线程-NSThread编程全面剖析_移动开发_编程_数据_课课家教育

  3.操作系统运行之后,操作系统就是指令集合躺在内存中了,现在程序中就躺着一些服务进程也就是一些服务指令的功能划分,它们时刻待命处理用户数据和操作,当有不同的操作和不同的数据是系统就会调用不同的的服务进程,说白了就是cpu从对应的服务指令所在的内存地址上取出指令执行,系统中有默认的几个服务进程,它们是使用计算机的基础。

系统中有默认的几个服务进程,它们是使用计算机的基础。

  4.当你点击应用程序的图标是,又会发生什么了,操作系统就用fork()一个进程,这个进程就作为点击应用程序的环境了,你点击的应用程序并不是一个劲的全部的往内存中塞,这就涉及到应用程序的运行原理了,在计算机中任何的数据都有自我描述的头部或文件,应用程序也不例外,这些头部有可能是公共的标准,也可能是自家平台上的定义的,说白了就是一流的公司定制最后标准化大家都遵循,应用程序为可执行文件,可以对应不同平台上的格式要求,Windows的exe,unix,Mac等。

  它们都在自己的可执行文件加了说明,通过可执行文件的头部,操作系统就知道把应用的那一段指令放到内存中,也就是找到入口函数。

  而可执行程序在编译是是基于虚拟内存的编译的,你只要想到在coding时,编译器不会蠢到用空间来存储可怕的字符串的,它都是通过虚拟内存地址来编译的也就是函数调用变量存储都是虚拟内存地址标识,在coding层面上我门看到的就是我们易读的字符串。

  计算机太傻了只认识 0-1,计算机通过约定的说明头找到了入口地址把它的一部分放到实际的内存地址中,实际内存地址的使用由操作系统管理有可能和虚拟地址不一样,当要运行的指令不在内存中时再去取,如果内存紧张就会进行内存叶的交换,这样就相当于一个进程就拥有整个内存空间了,内存的虚拟空间大小由硬件的寻址能力决定,这样就解决了应用程序的指令搬到内存中的问题了。

这样就相当于一个进程就拥有整个内存空间了,内存的虚拟空间大小由硬件的寻址能力决定,这样就解决了应用程序的指令搬到内存中的问题了。

  5.既然应用程序的指令被搬到了内存中和如何交换的文件解决,你就不用考虑要运行的指令还在硬盘上只是一部分用到的指令的问题(那些都是操作系统该干的事),现在你就假装应用程序的全部指令和数据资源都全部的搬到了内存中了,下面的任务就是CPU 表演时间了,进程建立就会默认的建立一个主要的线程栈来运行指令,应用程序的指令运行完之后进程就会被操作系统杀掉。

所为的多线程无非就是多个执行的线路,多几个线程栈

  6.所为的多线程无非就是多个执行的线路,多几个线程栈,它们共用进程资源,堆内存空间,线程栈保留了自己的运行状态信息,它要知道自己运行到什么地方了,CPU 寄存指令状态等,因为线程有运行时间片,所以就要记住自己的切换状态。

  可以用一个例子来说明,如果你是一个土财主,你有一个仆人A,对应单核CPU,今天的任务就是挑一百单水,一百捆柴,如果你说是顺序执行干不完一样不能干另外一样,这样的话就对应了应用程序的单线程设计,就顺序完成一百单水,一百捆柴;如果你说不管怎样只要你今天能干完就OK,你可以交替挑水捆柴,这样的话就对应应用程序的多线程设计,仆人有可能挑十挑水砍十捆柴,最后完成任务,对于单核CPU而言看不出什么高效的地方;

  现在你发财了你有买来了一个仆人B,现在你有两个仆人了,对应双核CPU相当于同一时间可以去执行指令比单核多线程设计时的线程时间片的切换高效。

  还是一样的任务,如果你现在叫A一个人去干,相当于应用程序是单线程设计,这样仆人B就闲置没事干,当A挑水挑了十挑的时候,你看这B闲暇着不爽你就叫他捆柴去,于是很快就完成任务了,对应程序在运行时创建出一个线程去完成别的任务的多线程设计,如果财主一开始就分配A去挑水,B去捆柴,就相当于应用程序在设计时把功能划分清楚分配给多个线程执行的设计,这样就充分的发挥了多核CPU的威力。

  7.多线程程序设计的最主要的注意事项是多线程对同一堆上的变量或者数据文件修改的冲突,从而影响结果,所以在设计时要特别小心。

  多线程在多核时代的今天已经非常成熟了,不管是移动设备还是台式电脑,它们的大体运行原理都大同小异,只要能灵活理解抽象成自己的知识储备,提升自己编程思维,为coding服务,所以一千个读者就有一千个哈姆雷特,同一个知识点不同的人有不同的知识储备就有不同的抽象理解思维,但其知识原理都一样。

  二.同步与异步

  同步和异步可以说是一种依赖关系,就不如两个线程A,B,当一个线程运行到一半时,就新建另一个线程B去完成某项任务,同步的话只有B线程运行完了之后A线程才往下运行,异步的话,A新建B线程之后继续执行,在iOS的app设计中UI的更新在主线程中执行,关于网络和数据处理的都放到非主线程执行,如果使用同步的话就会时主线程停止,影响用户体验

下面的任务就是CPU 表演时间了,进程建立就会默认的建立一个主要的线程栈来运行指令,应用程序的指令运行完之后进程就会被操作系统杀掉。

  三.NSThread详解

  1.首先来看NSThread的头文件

  /////////////////////////////////

  + (NSThread *)currentThread; //获得当前的线程如果是在主线程调用则的到主线程,否则就得到当前代码运行的线程,通过方法就可以的到线程并控制它

  + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

  + (BOOL)isMultiThreaded;//判断是否是多线程

  @property (readonly,retain)NSMutableDictionary *threadDictionary; //用于储存数据的

  + (void)sleepUntilDate:(NSDate *)date; //控制线程的运行时间用date指定

  + (void)sleepForTimeInterval:(NSTimeInterval)ti; //时间间隔来制定

  + (void)exit;//线程的退出

  + (double)threadPriority; //线程的优先级别,高的时间片就多

  + (BOOL)setThreadPriority:(double)p; //设置优先级别

  + (NSArray *)callStackReturnAddressesNS_AVAILABLE(10_5,2_0);//返回现场调用的地址数组,配合NSLog()使用能打印出线程栈的函数调用地址

  + (NSArray *)callStackSymbolsNS_AVAILABLE(10_6,4_0); //返回现场调用的名字数组,配合NSLog()使用能打印出线程栈的函数调用地址

  @property (copy)NSString *nameNS_AVAILABLE(10_5,2_0); //名字

  @property NSUInteger stackSizeNS_AVAILABLE(10_5,2_0);//线程栈大小

  @property (readonly)BOOL isMainThreadNS_AVAILABLE(10_5,2_0); //判断是否是主线程

  + (BOOL)isMainThreadNS_AVAILABLE(10_5,2_0);// reports whether current thread is main

  + (NSThread *)mainThreadNS_AVAILABLE(10_5,2_0); //得到主线程

  - (instancetype)initNS_AVAILABLE(10_5,2_0)NS_DESIGNATED_INITIALIZER;//初始化方法

  - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argumentNS_AVAILABLE(10_5,2_0);//初始化方法

  @property (readonly,getter=isExecuting)BOOL executingNS_AVAILABLE(10_5,2_0);//判断是否执行

  @property (readonly,getter=isFinished)BOOL finishedNS_AVAILABLE(10_5,2_0);//是否执行完成

  @property (readonly,getter=isCancelled)BOOL cancelledNS_AVAILABLE(10_5,2_0);//状态判断

  - (void)cancelNS_AVAILABLE(10_5,2_0);//撤销

  - (void)startNS_AVAILABLE(10_5,2_0);//开始

  - (void)mainNS_AVAILABLE(10_5,2_0);//线程入口函数

  @end

  /////////////////////////////////下面的几个函数是NSObject的扩展而已,说明下面的函数的实现是基于NSThread的多线程实现的

  @interface NSObject (NSThreadPerformAdditions)

  - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;

  - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

  - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)arrayNS_AVAILABLE(10_5,2_0);

  - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5,2_0);

  // equivalent to the first method with kCFRunLoopCommonModes

  - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)argNS_AVAILABLE(10_5,2_0);

  /////////////////////////////////

  2.NSThread的使用及详细说明

  类方法:

  + (NSThread *)currentThread; //该类方法可以获得当前代码运行的线程,通过该类方法你就能控制线程的运行退出状态改变状态属性,从而达到控制线程的作用

  + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; //该类方法的作用是从当前线程中分支出一条线程,而这条线程的入口函数为target实例变量的selector方法,因为线程都必须有一个入口函数,

  + (void)sleepUntilDate:(NSDate *)date;//运行的时间控制

  + (void)sleepForTimeInterval:(NSTimeInterval)ti;

  + (void)exit;//退出的类方法保证, 相当于[[NSThread currentThread]exit] ,

  + (double)threadPriority; //线程的优先级别设置,达到线程的时间片的分配 同exit方法一样

  + (BOOL)setThreadPriority:(double)p;//设置线程的优先级别

  + (NSArray *)callStackReturnAddresses //线程的调用都会有函数的调用函数的调用就会有栈返回地址的记录,在这里返回的是函数调用返回的虚拟地址,说白了就是在该线程中函数调用的虚拟地址的数组

  + (NSArray *)callStackSymbols //同上面的方法一样,只不过返回的事该线程调用函数的名字数字

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

  实例方法:

  - (void)cancel //取消函数

  - (void)start//线程开始运行函数

  - (void)main//线程的入口函数

  如果你子类化NSThread的话,你就可以把线程运行任务放到main函数中,这样你就可以通过start函数来手动的运行线程了,

  如果读者想通过NSThread来编写多线程应用时,要记住线程必须要有一个入口函数,这入口函数可以是实例变量的方法,也可以是main,把你要用多线程执行的任务写在入口函数中,你可以通过类方法[NSThread currentThread]来得到当前运行线程从而可以控制该线程了

  下面的函数是NSObject的扩展方法,它们都是基于NSThread来实现的

  - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;

  - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

  // equivalent to the first method with kCFRunLoopCommonModes

  - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)arrayNS_AVAILABLE(10_5,2_0);

  - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5,2_0);

  // equivalent to the first method with kCFRunLoopCommonModes

  - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg

  3.NSThread的使用

  a.方式一

  #import "ViewController.h"

  @interface ViewController ()

  @end

  @implementation ViewController

  {

  NSThread *thread;

  }

  - (void)viewDidLoad {

  [super viewDidLoad];

  // Do any additional setup after loading the view, typically from a nib.

  //新建一个线程,你要指定一个入口函数,和一个targert,下面新建的线程久是指定self中的netThread方法为该线程的入口函数,你只要把多线程实现的任务放到newThread方法中即可,质疑其他的属性读者可以自行调试

  thread= [[NSThread alloc]initWithTarget:self selector:@selector(newThread:) object:@"我是传过来的对象"];

  [thread start];//需要手动的启动,否则线程不会自动执行

  NSLog(@"我是主线程%@",[NSThread currentThread]);

  }

  -(void)newThread:(id)sender{

  NSLog(@"%@",sender);

  NSLog(@"我是新线程%@",[NSThread currentThread]);

  }

  运行结果:

  2015-01-01 18:09:57.845 threadTest[1251:58938] 我是主线程{number = 1, name = main}

  2015-01-01 18:09:57.845 threadTest[1251:58993] 我是传过来的对象

  2015-01-01 18:09:57.846 threadTest[1251:58993] 我是新线程{number = 2, name = (null)}

  b.方式二,通过类方法

  该方法只是把方法一中的新建启动包含到这个类方法中,输出结果不变

  [NSThread detachNewThreadSelector:@selector(newThread:) toTarget:self withObject:@"我是传过来的对象"];

  c.方法三,

  该方法是通过NSObject基于NSThread的扩展实现的输出结果不变

  [self performSelector:@selector(newThread:) withObject:@"我是传过来的对象"];

  还有其他的方法可以使用这里就不一一说明了,通过这三个方式可以总结出,多线程任务中必须有一个函数作为线程的入口函数,Target-selector-sender用来指定那一个target对象的方法selector作为入口函数传入什么参数sender作为传人线程的payload数据

  4.NSThread的子类化

  下面之类化NSThread,既是新建一个类继承NSThread并覆盖其main方法

  #import "MyThread.h"

  @implementation MyThread

  -(void)main{

  //覆盖main把多线程任务写在此处你可以通过delegate的语法方法把MyThread任务的执行状态通过代理方法传出去,入图片下载完之后通过代理通知delegate,并让它更新UI或者存储到磁盘等

  NSLog(@"我是myThread,你可以把任务写在这里哦");

  }

  @end

  

使用

MyThread *th = [[MyThreadalloc]init];

[th start];

 

  运行结果:

  2015-01-01 18:30:10.927 threadTest[1300:63866] 我是myThread,你可以把任务写在这里哦

  总结:NSThread编程入口函数,线程任务,[NSThread currentThread]的运用,UI的更新在主线程中更新,如果在其他线程更新UI不能及时看到效果,非UI的任务可以放到非主线程中执行。

  小分享:

  •NSThread:

  –优点:NSThread 比其他两个轻量级,使用简单

  –缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销

  •NSOperation:

  –不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上

  –NSOperation是面向对象的

  •GCD:

  –Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术

  –GCD是基于C语言

  什么是IOS:

  IOS(Internetworking Operating System-Cisco,缩写IOS),CISCO网络配置系统。

  IOS是一个为网际互连优化的复杂的操作系统——类似一个局域操作系统(NOS)、如 Novell的NetWare,为LANs而进行优化。

  IOS为长时间经济有效地维护一个互联网络提供一下统一的规则。简而言之,它是一个与硬件分离的软件体系结构,随 网络技术的不断发展,可动态地升 级以适应不断变化的技术(硬件和软件)。

  IOS可以被视作一个网际互连中枢:一个高度智能的管理员,负责管理的控制复杂的 分布式网络资源的功能。

  IOS为客户提供信息基础设施的投资保护。IOS今天支持的许多 特性是大多数客户未来需要的特性。

  随着一家公司的成长扩展到新的领地,随着兼并收购带来的基础机构复杂性以及协议转换或新流量模式的出现,IOS提供的体系结构能使机构灵活地应用变化和经济有效地进行扩展以满足新的需求。

  IOS允许我们的客户迅速调节适应新的模式,更长时间地保持其信息基础机构投资;其结果是随时间推移提供投资保护和降低拥有成本。

     小结:相信最后大家阅读完毕本篇文章后一定学到了不少知识吧?其实大家私下还得多多自学,才能学习到更多的知识哦~当然如果还有什么问题欢迎登录课课家教育平台咨询哦~

赞(0)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程