note:文明看帖转载是对自己的尊重也是对学者的鼓励,欢迎批评讨论

iOS多线程-NSThread编程详解


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

一.进程与线程


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


1.进程就是当你点击应用程序的图标时,系统会为你创建一个用于运行该程序的进程,你的应用程序在神通广大也无法脱离操作系统的手掌心,在计算机世界里任何都遵循标准,有自描述,应用程序之间,操作系统之间应用程序之间都通过标准来交流,没有标准的计算机将寸步难行,进程是操作系统分配给你的运行环境,应用程序可以通过进程和操作系统交互(系统调用,反过来操作系统可以控制进程的生命周期和计算机硬件资源分配);线程则是进程中的一条执行路径,它拥有自己的运行栈状态机制,可以获得CPU运行时间片段。一个进程中可以有多个的线程,从而就会产生同步和异步的工作机制。(从编程的角度只要知道其运行原理并根据自己的知识储备抽象成自己的思维编程模式,最后都是为coding服务,至于实现细节原理应该是系统设计者所考虑的问题,所以各个人有不同的理解):

2.操作系统在没有启动的时候,它并没有在内存中,它就是静静地躺在磁盘上,当你按下电源键时,先运行的是内嵌到硬件上的BIOS,是它把操作系统搬到内存中(至于它怎么搬的这是硬件厂商和操作系统厂商或者是协议好的,作为软件编程层面,只要了解是那么回事就行)再把计算机的使用权力交给操作系统,操作系统就负责管理分配计算机资源,而用户要想使用计算机硬件资源就要需要通过操作系统这个大管家,如果大家感兴趣推荐这本书《自己动手写操作系统》这本书主要讲的就是操作系统的实现,通过它你能了解到计算机底层的运行原理,我们不需要熟读只要了解大意提升自己的编程思维就行。

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的更新在主线程中执行,关于网络和数据处理的都放到非主线程执行,如果使用同步的话就会时主线程停止,影响用户体验

三.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] 我是主线程<NSThread: 0x7fc0da513770>{number = 1, name = main}

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

2015-01-01 18:09:57.846 threadTest[1251:58993] 我是新线程<NSThread: 0x7fc0da70dd60>{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
<p class="p1"><span class="s1"></span>使用</p><p class="p2"><span class="s1">    </span><span class="s2">MyThread</span><span class="s1"> *th = [[</span><span class="s2">MyThread</span><span class="s1"> </span><span class="s3">alloc</span><span class="s1">]</span><span class="s3">init</span><span class="s1">];</span></p><p class="p2"><span class="s1">    [th </span><span class="s3">start</span><span class="s1">];</span></p>

运行结果:

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

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

转载于:https://www.cnblogs.com/fanyiyao-980404514/p/4207426.html

iOS-NSThread编程详解相关推荐

  1. IOS 多线程04-GCD详解 底层并发 API

    IOS 多线程04-GCD详解 底层并发 API 注:本人是翻译过来,并且加上本人的一点见解. 前言 想要揭示出表面之下深层次的一些可利用的方面.这些底层的 API 提供了大量的灵活性,随之而来的是大 ...

  2. iOS 2D绘图详解(Quartz 2D)之路径(点,直线,虚线,曲线,圆弧,椭圆,矩形)

    前言:一个路径可以包含由一个或者多个shape以及子路径subpath,quartz提供了很多方便的shape可以直接调用.例如:point,line,Arc(圆弧),Curves(曲线),Ellip ...

  3. PHP SOCKET编程详解

    这篇文章主要介绍了PHP SOCKET编程详解,需要的朋友可以参考下 1. 预备知识 一直以来很少看到有多少人使用php的socket模块来做一些事情,大概大家都把它定位在脚本语言的范畴内吧,但是其实 ...

  4. Linux网络编程---详解TCP

    Linux网络编程---详解TCP的三次握手和四次挥手_shanghx_123的博客-CSDN博客_tcp的协议数据单元被称为 TCP协议详解(TCP报文.三次握手.四次挥手.TIME_WAIT状态. ...

  5. MFC下CSocket编程详解

    MFC下CSocket编程详解:  1. 常用的函数和注意事项(详细的函数接口说明请查看MSDN): CSocket::Create 初始化(一般写服务器程序都不要用为好,用下面的 CSocket:: ...

  6. Linux串口编程详解

    Linux串口编程详解(阻塞模式.非阻塞模式.select函数) 之前一直觉得串口编程很简单,这两天仔细研究后发现串口里的各种参数还挺复杂,稍不注意就容易出错,这里总结一下网上的各种文章及自己的理解与 ...

  7. [顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功)...

    [顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功) 原文:[顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之 ...

  8. [进阶] --- Python3 异步编程详解(史上最全篇)

    [进阶] - Python3 异步编程详解:https://blog.csdn.net/lu8000/article/details/45025987 参考:http://aosabook.org/e ...

  9. Java高并发编程详解系列-Java线程入门

    根据自己学的知识加上从各个网站上收集的资料分享一下关于java高并发编程的知识点.对于代码示例会以Maven工程的形式分享到个人的GitHub上面.   首先介绍一下这个系列的东西是什么,这个系列自己 ...

最新文章

  1. linux elf格式文件详细分析
  2. linux恢复出厂设置_怎么恢复tp-link路由器出厂设置 恢复tp-link出厂设置方法【详解】...
  3. 成功解决AttributeError: ‘Series‘ object has no attribute ‘columns‘
  4. spring 配置jdbc/hibernate/jpa
  5. Sql Server 查询语句
  6. 虚拟机centos7.3不能启动
  7. 95-50-050-java.nio.channels-NIO-NIO之Channel(通道)
  8. C++---------【多态性】之【静态多态性】
  9. windows下搭建SSH隧道内网映射
  10. PyCharm社区版设置简体中文界面
  11. 品牌对比 蜜雪冰城 VS 喜茶
  12. OpenCV的各种矩阵基本运算、基本操作及示例代码(加、减、乘、点乘、点除、乘方、开方、累加、转置、比较等)
  13. arm linux建站,arm服务器做虚拟机(arm平台虚拟机)
  14. Vuex-状态管理(24)
  15. unity背景设置透明,显示ios原生ui背景
  16. android Button美化
  17. java 调节color亮度算法_照片处理-饱和度调节
  18. 【物联网项目系列】springboot 实现mqtt物联网
  19. python 淘宝客服 机器人_Python实现资源代下|实现机器值守|全自动淘宝自动发货机器人...
  20. AngularJS实现动态添加输入控件功能

热门文章

  1. 从零点五开始用Unity做半个2D战棋小游戏(二)
  2. HTTP状态码对照表(全部状态_建议收藏)
  3. mysql 如何清除sql缓存
  4. N秒钟后自动跳转的html页面,javascript
  5. C++main函数的参数介绍以及如何在main函数前执行一段代码
  6. Java 内存模型(一)
  7. c++输入输出流加速器
  8. 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架
  9. WSGI、uwsgi和uWSGI
  10. android6.0的坑