有些程序是一条直线,起点到终点;有些程序是一个圆,不断循环,直到将它切断。
(1).直线的如简单的Hello World,运行打印完,它的生命周期便结束了,像昙花一现那样;
(2).圆如操作系统,一直运行直到你关机。
一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。Mac和iOS中的程序启动,创建好一个进程的同时, 一个线程便开始运行,这个线程叫主线程。主线程在程序中的地位和其他线程不同,它是其他线程最终的父线程,且所有界面的显示操作即AppKit(Mac OS X开发)或 UIKit(iOS开发)的操作必须在主线程进行。系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则共用进程的内存空间。每创建一个新的线程,都需要一些内存(如每个线程有自己的Stack(堆)空间)和消耗一定的CPU时间。另外当多个线程对同一个资源出现争夺的时候需要注意线程安全问题。
创建线程:
在iOS中,创建一个线程有3中方法:
(1)使用NSThread
(2)NSOperation
(3)GCD (Grand Central Dispatch)
比较常用的方法是,使用 NSThread 创建线程。
NSThread提供了创建线程的途径,还可以提供了检测当前线程是否是主线程的方法。 使用NSThread创建一个新的线程有两种方式:
(1).创建一个NSThread的对象,调用其start方法。对于这种方式的NSThread对象的创建,可以使用一个目标对象的方法初始化一个 NSThread对象,或者创建一个继承NSThread类的子类,实现其main方法,然后在直接创建这个子类的对象。
NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil];
[myThread start]; //启动线程
(2).使用 detachNewThreadSelector:toTarget:withObject:这个类方法创建一个线程,这个比较直接了,直接使用目标对象的方法 作为线程启动入口。
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];
2.使用NSOperation&NSOperationQueue:
很多时候我们使用多线程,需要控制线程的并发数,毕竟线程也是消耗系统资源的,当程序中同时运行的线程过多时,系统必然变慢。 所以很多时候我们会控制同时运行线程的数目。
NSOperation可以封装我们的操作,然后将创建好的NSOperation对象放到NSOperationQueue中,OperationQueue便开始启动新的线程去执行队列中的操作,OperationQueue的并发度是可以通过如下方式进行设置:
- (void)setMaxConcurrentOperationCount:(NSInteger)count
3.使用GCD:
// 后台执行:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
});
// 主线程执行:
dispatch_async(dispatch_get_main_queue(), ^{
// something
});
// 一次性执行:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});
// 延迟2秒执行:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});
@interface NSObject (NSThreadPerformAdditions) - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUnti
首先循环体的开始需要检测是否有需要处理的事件,如果有则去处理,如果没有则进入睡眠以节省CPU时间。 在RunLoop中,需要处理的事件分两类,一种是定时器,一种是输入源。定时器就是那些需要定时执行的操作。输 入源主要有:performSelector源。
在一个线程中,需要同时处理很多事情。如:需要处理定时钟事件,需要处理用户的触控事件,需要接受网络远端发过来的数据。将这些需要做的事情统统注册到输入源中,每次循环的开始便去检查这些输入源是否有需要处理的数据,有的话则去处理。
拿具体的应用举个例子,NSURLConnection网络数据请求,默认是异步的方式,其实现原理就是创建之后将其作为输入源加入到当前的 RunLoop中,而等待网络响应以及网络数据接受的过程则在一个新创建的独立的线程中完成。当这个线程处理到某个阶段的时候,比如得到对方的响应或者接受完了网络数据之后,便通知之前的线程去执行其相关的delegate方法。
在Cocoa中经常看到scheduleInRunLoop:forMode: 这样的方法,这个便是将其加入到输入源中,当检测到某个事件发生的时候,相关的delegate方法便被调用。对于CoreFoundation这一层而 言,通常的模式是创建输入源,然后将输入源通过CFRunLoopAddSource函数加入到RunLoop中,相关事件发生后,对应的回调函数会被调 用。
高级iOS开发技术交流群:415239068,欢迎大家加入