iOS多线程详解

/**

 无论使用哪种多线程技术都可以使用

 [NSThread currentThread]跟踪查看当前执行所在的线程情况。

 num = 1表示在主线程上执行的任务

 

 ================================================================

 1. NSObject多线程技术

 

 1> 使用performSelectorInBackground可以开启后台线程,执行selector选择器选择的方法

 2> 使用performSelectorOnMainThread可以重新回到主线程执行任务,通常用于后台线程更新界面UI时使用

 3> [NSThread sleepForTimeInterval:1.0f];

    让当前线程休眠,通常在程序开发中,用于模拟耗时操作,以便跟踪不同的并发执行情况!

 

    但是:在程序发布时,千万不要保留此方法!不要把测试中的代码交给客户,否则会造成不好的用户体验。

 

 提示:使用performSelectorInBackground也可以直接修改UI,但是强烈不建议使用。

 

 注意:在使用NSThread或者NSObject的线程方法时,一定要使用自动释放池,否则容易出现内存泄露。

代码演示:

(1)NSObject开辟子线程

 [self performSelectorInBackground:@selector(bigDemo)withObject:nil];

(2)用NSThread方法开辟子线程

   2.1// 类方法新建一个线程,调用@selector方法

   [NSThread detachNewThreadSelector:@selector(bigDemo)toTarget:self withObject:nil];

     2.2//成员方法

    NSThread *thread =        [[NSThread alloc]initWithTarget:self selector:@selector(bigDemo)object:nil];

    // 启动start线程

    [thread start];

被子线程调用的方法:

- (void)bigDemo

{

    // 自动释放池

    // 负责其他线程上的内存管理,在使用NSThread或者NSObject的线程方法时,一定要使用自动释放池

    // 否则容易出现内存泄露。

    @autoreleasepool {

       

        NSLog(@"%@", [NSThread currentThread]);

        // 模拟网络下载延时,睡眠1秒,通常是在开发中测试使用。

        [NSThread sleepForTimeInterval:1.0f];

        //强烈不建议直接在后台线程更新界面UI

        // 模拟获取到下载的图像

        UIImage *image = [UIImage imageNamed:@"头像1"];

        // 在主线程更新图像

        // 使用self调用updateImage方法在主线程更新图像

            [self performSelectorOnMainThread:@selector(updateImage:)withObject:image waitUntilDone:YES];

        // 使用imageViewsetImage方法在主线程更新图像

        [_imageView performSelectorOnMainThread:@selector(setImage:)withObject:image waitUntilDone:YES];

        

    }

}

被主线程调用的方法:

- (void)updateImage:(UIImage *)image

{

    NSLog(@"更新图像-> %@", [NSThread currentThread]);

    

    _imageView.image = image;

}

************************************************************

************************************************************

************************************************************

************************************************************

1GCD演练是基与C语言的NSOPeration&NSOperationQueue基于OC框架写的 

• NSOperation的两个子类NSInvocationOperation、NSBlockOperation

工作原理:NSOperation封装要执⾏行的操作

将创建好的NSOperation对象放NSOperationQueue

启动OperationQueue开始新的线程执⾏行队列中的操作

1.1#pragma mark invocation

- (IBAction)operationDemo1

{

    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(opAction)object:nil];

    

    // 如果使用start,会在当前线程启动操作

//    [op1 start];

    

    // 1. 一旦将操作添加到操作队列,操作就会启动

    [_queue addOperation:op1];

}

#pragma mark - NSOperation演练

- (void)opAction

{

    NSLog(@"%@", [NSThread currentThread]);

    

    // 模拟网络加载延时

    [NSThread sleepForTimeInterval:1.0f];

    

    // 模拟获取到图像

    UIImage *image = [UIImage imageNamed:@"头像1"];

    

    // 设置图像,在主线程队列中设置

    [[NSOperationQueue mainQueueaddOperationWithBlock:^{

        _imageView.image = image;

    }];

}

、、、、、、、、、、、、、、、、、、、

1.2#pragma mark blockOperation

- (IBAction)operationDemo2

{

    // block的最大好处,可以将一组相关的操作,顺序写在一起,便于调试以及代码编写

    [_queue addOperationWithBlock:^{

        NSLog(@"%@", [NSThread currentThread]);

        

        // 模拟延时

        [NSThread sleepForTimeInterval:1.0f];

        

        // 模拟获取到图像

        UIImage *image = [UIImage imageNamed:@"头像1"];

        

        // 设置图像,在主线程队列中设置

        [[NSOperationQueue mainQueueaddOperationWithBlock:^{

            _imageView.image = image;

        }];

    }];

}

//invocationblockOperation区别就是一个是用方法调用,一个是代码块调用


1.3、NSOperation对象放NSOperationQueue牛逼之处


#pragma mark 依赖关系,可以使子线程按顺序执行

- (IBAction)operationDemo3:(id)sender

{

     _queue=[[NSOperationQueue alloc]init];

    // 1. 下载

    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"下载 %@" , [NSThread currentThread]);

    }];

    // 2. 滤镜

    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"滤镜 %@" , [NSThread currentThread]);

    }];

    // 3. 显示

    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"更新UI %@" , [NSThread currentThread]);

    }];

    

    // 添加操作之间的依赖关系,所谓依赖关系,就是等待前一个任务完成后,后一个任务才能启动

    // 依赖关系可以跨线程队列实现

    [op2 addDependency:op1];

    [op3 addDependency:op2];

    // 提示:在指定依赖关系时,注意不要循环依赖,否则不工作。

    //[op1 addDependency:op3];

    

    [_queue addOperation:op1];

    [_queue addOperation:op2];

    [[NSOperationQueue mainQueueaddOperation:op3];

}

1.4、最大并发线程数量,这是NSOperation独有功能

- (IBAction)operationDemo4

{

    // 控制同时最大并发的线程数量

    [_queue setMaxConcurrentOperationCount:2];

    

    for (NSInteger i =0; i <200; i++) {

        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{

            NSLog(@"%@", [NSThread currentThread]);

        }];        

        [_queue addOperation:op];

    }

}

****************************************************************************************************************************************************************************************************************************************

********************************************************************************************************************

2GCD演练是基与C语言的框架写的

- (IBAction)gcdDemo1

{

    /**

     GCD就是为了在多核上使用多线程技术

     

     1> 要使用GCD,所有的方法都是dispatch开头的

     2> 名词解释

        global  全局

        queue   队列

        async   异步:执行控制不住先后顺序

        sync    同步:主要用来控制方法的被调用的顺序

     

     3> 要执行异步的任务,就在全局队列中执行即可

        dispatch_async 异步执行控制不住先后顺序

     

     4> 关于GCD的队列

        全局队列    dispatch_get_global_queue

            参数:优先级 DISPATCH_QUEUE_PRIORITY_DEFAULT

                 始终是 0

        串行队列

        主队列

     

     5> 异步和同步于方法名无关,与运行所在的队列有关!

        提示:要熟悉队列于同步、异步的运行节奏,一定需要自己编写代码测试!

     

        同步主要用来控制方法的被调用的顺序

     */

    // 1. 队列

   dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    

    // 2. 将任务异步(并发)执行

    dispatch_sync(queue, ^{

        NSLog(@"a->%@", [NSThread currentThread]);//同步在哪个线程,它执行的方法就在哪个线程

    });

    dispatch_async(queue, ^{

        NSLog(@"b->%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

        dispatch_sync(queue, ^{

            NSLog(@"我哦我哦我->%@", [NSThread currentThread]);

        });

        //"我哦我哦我"所在的线程就是这个子线程不不不不不不不不不所在的线程

        NSLog(@"不不不不不不不不不->%@", [NSThread currentThread]);

    });

    //GCD回到主线程的时候必须是异步的

    dispatch_async(dispatch_get_main_queue(), ^{

        NSLog(@"main - > %@", [NSThread currentThread]);

    });//dispatch_sync方法不能在主队列中调用,因为这会无限期的阻止线程并会导致你的应用死锁。所有通过GCD提交到主队列的任务必须是异步的。


}

2.1#pragma mark 串行队列,里面的操作按顺序执行

create方法,获得的是自己新建的线程,所以create需要我们释放线程(如果是手动管理内存的话)

 //dispatch_queue_t用来引导队列变量

    //createQueue 就是一个队列变量

    //dispatch_queue_create是手动创建队列的方法,两个参数,第一个参数是这个队列的名,第二个参数如果写DISPATCH_QUEUE_SERIAL表示这是一个顺序的队列,这个队列里的方法顺序执行。如果是DISPATCH_QUEUE_CONCURRENT,表示这是一个并行的队列,则这个队列里的方法同时执行

    

       //create出来的默认为default优先级

    

    //用完之后,要释放

    //dispatch_release(createQueue);

- (void)makeGCD_Queue

{

    //串行队列自行创建,不能get

    dispatch_queue_t queue=dispatch_queue_create("Myqueue",DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"1");

        }

        NSLog(@"a->%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

        NSLog(@"b->%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"2");

        }

        NSLog(@"c->%@", [NSThread currentThread]);

    });

    //GCD回到主线程的时候必须是异步的

    dispatch_async(queue, ^{

        //必须将这个回到主线程的方法,放在这个队列中的一个请求方法,才会按照顺序执行

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"d->%@", [NSThread currentThread]);

            

        });//dispatch_sync方法不能在主队列中调用,因为这会无限期的阻止线程并会导致你的应用死锁。所有通过GCD提交到主队列的任务必须是异步的。

        

    });

}

2.2#pragma mark GCD队列组,并发执行所有任务,所有任务执行完毕后回主队列

- (void)makeGCD_Group {

    dispatch_group_t group =dispatch_group_create();

    dispatch_queue_t queue =dispatch_get_global_queue(0,0);

    dispatch_group_async(group, queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"1");

        }

        NSLog(@"a->%@", [NSThread currentThread]);

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@“b->%@“, [NSThread currentThread]);

    });

    dispatch_group_async(group, queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"2");

        }

        NSLog(@“c->%@“, [NSThread currentThread]);

    });

    //Group队列任务组,并发执行任务组中的任务,全部执行完毕后,再最后用notify告知所有任务完成,并做相应处理

    dispatch_group_notify(group,dispatch_get_main_queue(), ^{

        NSLog(@“d->%@“, [NSThread currentThread]);

    });

    

}


3全局队列和并发队列的区别:

    1、全局队列没有名字,但是并发队列有名字。有名字可以便于查看系统日志

    2、全局队列是所有应用程序共享的。

    3、在mrc的时候,全局队列不用手动释放,但是并发队列需要。

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值