iOS线程

一, iOS有三种多线程编程的技术,分别是:

1.、NSThread 

2、Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue的使用

3、GCD  全称:Grand Central Dispatch( iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用

这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。

二,NSThread

[NSThread currentThread]

1,创建NSThread有两种方式:

1)-(id)initWithTarget:(id)target selector:(SEL) selector object:(id)arg:  创建一个新线程对象;

调用start方法启动线程;

注意:启动线程使用start方法,线程启动之后并不是立即进入运动状态,线程被启动后处于就绪状态,,当系统调度线程后,线程才会进入运行状态。

2)、

[NSThread detachNewThreadSelector:@selector(firstCounter)

                             toTarget:self

                           withObject:nil]; //创建并启动新线程

3)、[self performSelectorInBackground:@selector(firstCounter) withObject:nil];

performSelectorInBackground方法为我们在后台创建了一个线程。这等同于 我们为 selectors 创建一个新的线程。 但是要记住,必须在调用的方法中加上自动释放池!

// 任务1

- (void) firstCounter{

    @autoreleasepool {

        // MyCode

    

    }

}

来释放掉我们在操作过程中的内存!否则会发生内存泄漏!
 
 
对于使用线程的一些建议:
 
1.当我们需要中途停止线程时,我们不应该调用exit方法,而是调用cancel方法。因为,如果我们直接调用
exit方法的话,线程是直接退出,而没有机会去执行清理操作,可能会产生内存泄漏!
 
2.我们必须要清楚这么一个现象!
当线程在执行过程中,如果被sleepForTimeInterval后,线程将会被进入休眠。那么在它休眠期间又被cancel后,那么,事实上,线程在醒来后,任然会执行完它的操作
http://blog.sina.com.cn/s/blog_7b9d64af0101cajz.html
 
补充:
1,如果程序希望调用子线程的start方法后子线程立即开始执行,程序可以使用[NSThread sleepForTimeInterval:0.01],让当前主线程睡眠1毫秒,CPU就会去执行另外一个就绪状态的线程,这样子线程就能立即执行;
 
三,NSOperation

NSOperation是苹果封装的一套多线程的东西,不像GCD是纯C语言的,这个是OC的。但相比较之下GCD会更快一些,但本质上NSOPeration是多GCD的封装。

NSOperation与GCD的比较

GCD是基于c的底层api,NSOperation属于object-c类。ios首先引入的是NSOperation,IOS4之后引入了GCD和NSOperationQueue并且其内部是用gcd实现的。GCD优点:GCD主要与block结合使用。代码简洁高效。执行效率稍微高点。

NSOperation相对于GCD:

1,NSOperation拥有更多的函数可用,具体查看api。NSOperationQueue 是在GCD基础上实现的,只不过是GCD更高一层的抽象。

2,在NSOperationQueue中,可以建立各个NSOperation之间的依赖关系。

3,NSOperationQueue支持KVO。可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)

4,GCD 只支持FIFO 的队列,而NSOperationQueue可以调整队列的执行顺序(通过调整权重)。NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。使用NSOperation的情况:各个操作之间有依赖关系、操作需要取消暂停、并发管理、控制操作之间优先级,限制同时能执行的线程数量.让线程在某时刻停止/继续等。使用GCD的情况:一般的需求很简单的多线程操作,用GCD都可以了,简单高效。从编程原则来说,一般我们需要尽可能的使用高等级、封装完美的API,在必须时才使用底层API。当需求简单,简洁的GCD或许是个更好的选择,而Operation queue 为我们提供能更多的选择

 

GCD

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。

dispatch queue分成以下三种:

1)运行在主线程的Main queue,通过dispatch_get_main_queue获取。

复制代码
/*!
* @function dispatch_get_main_queue
*
* @abstract
* Returns the default queue that is bound to the main thread.
*
* @discussion
* In order to invoke blocks submitted to the main queue, the application must
* call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main
* thread.
*
* @result
* Returns the main queue. This queue is created automatically on behalf of
* the main thread before main() is called.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT struct dispatch_queue_s _dispatch_main_q;
#define dispatch_get_main_queue() \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q)
复制代码

可以看出,dispatch_get_main_queue也是一种dispatch_queue_t。

2)并行队列global dispatch queue,通过dispatch_get_global_queue获取,由系统创建三个不同优先级的dispatch queue。并行队列的执行顺序与其加入队列的顺序相同。

3)串行队列serial queues一般用于按顺序同步访问,可创建任意数量的串行队列,各个串行队列之间是并发的。

当想要任务按照某一个特定的顺序执行时,串行队列是很有用的。串行队列在同一个时间只执行一个任务。我们可以使用串行队列代替锁去保护共享的数据。和锁不同,一个串行队列可以保证任务在一个可预知的顺序下执行。

serial queues通过dispatch_queue_create创建,可以使用函数dispatch_retain和dispatch_release去增加或者减少引用计数。

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 });
// 自定义dispatch_queue_t dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL); dispatch_async(urls_queue, ^{    // your code }); dispatch_release(urls_queue);
// 合并汇总结果 dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ // 并行执行的线程一 }); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ // 并行执行的线程二 }); dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{ // 汇总结果 });
复制代码

一个应用GCD的例子:

//将代码块提交给系统的全局并发队列   
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
        NSError * error;
        NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
        if (data != nil) {
//将代码块提交给主线程关联的队列,该代码块将由主线程完成
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"call back, the data is: %@", data);
            });
        } else {
            NSLog(@"error when download:%@", error);
        }
    });

GCD的另一个用处是可以让程序在后台较长久的运行。

在没有使用GCD时,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是在使用GCD后,app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。

让程序在后台长久运行的示例代码如下:

复制代码
// AppDelegate.h文件
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;

// AppDelegate.m文件
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self beingBackgroundUpdateTask];
    // 在这里加上你需要长久运行的代码
    [self endBackgroundUpdateTask];
}

- (void)beingBackgroundUpdateTask
{
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self endBackgroundUpdateTask];
    }];
}

- (void)endBackgroundUpdateTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
    self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
复制代码
 
 
*****************
补充:
 创建队列:
1,dispatch_queue_t  dispatch_get_current_queue(void):获取当前执行代码所在的队列
2,dispatch_queue_t dispatch_get_global_queue(long priority,unsigned long flags)根据指定优先级、额外的旗标来获取系统全局并发队列;
第一个参数long priority可接受DISPATCH_QUEUE_PRIORITY_HIGH(2),DISPATCH_QUEUE_PRIORITY_DEFAULT(0),DISPATCH_QUEUE_PRIORITY_LOW()-2,
DISPATCH_QUEUE_PRIORITY_BACKGROUND这几个优先级。
目前,flags暂未使用,只是为将来做准备的,一般传入0即可。
3,dispatch_queue_t dispatch_get_main_queue(void):获取应用主程序所关联的串行队列;
4,dispatch_queue_t dispatch_queue_create(const char *label,dispatch_queue_attr_t attr);根据制定字符串标签创建队列;
第二个参数dispatch_queue_attr_t attr 可控制创建串行队列还是并发队列,如果将第二个参数设为"DISPATCH_QUEUE_SERIAL",则代表串行队列,如果将第二个参数设为
"DISPATCH_QUEUE_CONCURRENT",则代表并发队列;在没有启动ARC机制的情况下,通过这种方式创建的队列需要调用dispatch_release()函数释放引用技术;
 
根据以上函数,程序可以创建如下几种队列。
1)获取系统默认的全局并发队列;
2)获取系统住线程关联的串行队列;
dispatch_queue_t queue = dispatch_get_main_queue();
如果将任务提交给主线程关联的串行队列,那么就相当于直接在主线程中去执行任务。
3)创建串行队列;
4)创建并发队列;
 
 
异步提交任务
 
void dispatch_async(dispatch_queue_t queue ,dispatch_block_t block);
将代码块以异步的方式提交给指定队列,该队列底层的线程池将负责执行该代码块。
void dispatch_async_f(dispatch_queue_t queue,void *context,dispatch_function_t work);
将函数以异步的方式提交给指定队列,该队列底层的线程池将负责执行该函数。
void dispatch_sync(dispatch_queue_t queue ,dispatch_block_t block); //同步
void dispatch_sync_f(dispatch_queue_t queue,void *context,dispatch_function_t work);
void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block)
将代码块以异步的方式提交给指定队列,该队列底层的线程池负责在when指定的时间点执行该代码块。
void dispatch_after_f(dispatch_time_t when,dispatch_queue_t queue,void *context,dispatch_function_t work)
void dispatch_apply(size_t iterations,dispatch_queue_t queue,void(^block)(size_t));
将代码块以异步的方式提交给指定队列,该队列底层的线程池将多次重复执行该代码块。
void dispatch_apply_f(size_t iterations,dispatch_queue_t queue,void *context,void (*work)(void*,size_t));
void disaptch_once(dispatch_once_t *predicate,dispatch_block_t block);
将代码块提交给指定队列,该队列底层的线程池控制在应用的某个生命周期内执行该函数一次;
系统将直接用主线程执行该函数提交的代码块;可能阻塞主线程;
 
posted @ 2015-02-08 23:49  尘恍若梦  阅读(363)  评论(0编辑  收藏  举报