block、多线程与GCD总结

block



//        int num = 10;
//        MyBlock block1 = ^{
//            NSLog(@"num is %d",num);
//        };
//        num = 20;
//        block1();// 此时打印出来的结果是 10 ;因为 num block 编译时已经作为常量被编译进去了,就不会再被修改
       
       
/*
        
局部的基本数据类型的变量,进入到 block 中, num 会变成常量,如果需要在 block 中对 num 进行修改,需要加上关键字 __block (也可以用 static 关键字进行修饰)
         */

       
__block int num1 = 10 ;
       
MyBlock block1 = ^{
           
NSLog ( @"num1 is %d" ,num1); //20
            num1++;
//21
        };
        num1 =
20 ;
        block1();
       
NSLog ( @"num1 is %d" ,num1); //21
       
       
       
/*
        
局部的 OC 对象进入到 block 时,该对象会被 retain 一次(注意: block 在堆区上时才会起到 retain 作用)
        
必须进行一次 copy 操作
         */

       
__block NSObject *obj = [[ NSObject alloc ] init ];
       
NSLog ( @"%ld" ,obj. retainCount );
       
MyBlock block2 =  ^{
           
NSLog ( @"%ld" ,obj. retainCount );
        };
        [block2
copy ]; // 当没有 copy 时, block 块中的 obj.retainCount 1 copy 之后为 2
       
// 但是当用 __block 修饰时,即使 copy 后, block 块中的 obj.retainCount 也为 1
        block2();
       
       
__block Person *ps = [[ Person alloc ] init ]; // 当没有产生循环引用时,可以不使用 __block 修饰,但是一旦产生循环引用,就必须使用 __block 修饰
       
       
MyBlock block3 = ^{
           
NSLog ( @"%ld" ,ps. retainCount );
        };
        [block3
copy ];
        block3();
       
        [ps
release ]; // 当没有 __block 修饰时, dealloc 方法不会调用
        [block3 release];
2.
main函数中      
Person *ps = [[ Person alloc ] init ];
[ps testFun ];
[ps release];
Person的.m文件中
- ( void )testFun
{
   
_personObj = [[ NSObject alloc ] init ];
   
   
self . myBlock = ^{
       
NSLog ( @"obj retainCount is %ld" , _personObj . retainCount ); //1  原因:在 block 中引用一个实例变量时,该实例所在的对象会被 retain 一次,而 _personObj ps 的实例变量,所以当在 block 里面引用 _personObj 时, ps 会被 retain 一次,此时 myBlock 会持有 Person 的对象 ps ,而 myBlock 本身是被 Person 的对象 ps 持有,
       
// 因此就会形成 self--myBlock--self 的循环引用
    };
   
_myBlock ();
}
//- (void)testFun
//{
//    NSObject *obj = [[NSObject alloc] init];
//
//    self.myBlock = ^{
//        NSLog(@"obj retainCount is %ld",obj.retainCount);//2
//    };
//    _myBlock();
//}
/*
 
block 里面引用一个局部的 OC 对象,该对象会被 retain 一次
 
如果使用 __block 修饰局部变量,则不会被 retain
 */

//- (void)testFun
//{
//    __block NSObject *obj = [[NSObject alloc] init];
//   
//    self.myBlock = ^{
//        NSLog(@"obj retainCount is %ld",obj.retainCount);//1,
//    };
//    _myBlock();
//}

多线程与GCD



1.程序启动
程序的执行依赖于主线程
2.进程的基本概念
     每一个进程都有一个应用程序,都有独立的内存空间,一般来说一个应用程序存在一个进程,但也有多个进程的情况
     同一个进程中的线程共享内存中的内存和资源
3. 多线程的基本概念
     每一个程序都有一个主线程,程序启动时创建(调用main函数来启动)
     主线程的生命周期是和应用程序绑定的,程序退出(结束)时,主线程也就停止了
     多线程技术表示,一个应用程序有多个线程,使用多线程能提高CPU的使用率,防止主线程堵塞
     任何有可能堵塞主线程的任务都不要在主线程执行(访问网络)
4. 判断当前线程是否是主线程
BOOL bool1 = [[ NSThread currentThread ] isMainThread ];
    NSLog(@"bool1 is %d",bool1);
5.创建线程的六种方式
//    // 第一种创建线程的方式
//    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
//    // 第一种方式必须手动开启
//    [thread1 start];
   
   
   
// 第二种方式
//    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

   
   
// 第三种方式
//    [self performSelectorInBackground:@selector(run) withObject:nil];
   
   
// 第四种方式 NSOperationQueue 是一个操作队列或线程池
   
NSOperationQueue *queue = [[ NSOperationQueue alloc ] init ]; // 不能使用当前 queue 即下面一行代码,那样会使得线程变为主线程
//    NSOperationQueue *queue = [NSOperationQueue currentQueue];
   
//    // 往队列中添加一个操作
//    [queue addOperationWithBlock:^{
//        [self run];
//    }];
   
   
   
//    // 第五种方式 , queue 中添加 operation 但一般不要直接创建 NSOperation 的对象而是创建 NSInvocationOperation 的对象, NSInvocationOperation 继承自 NSOperation
//    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//    [queue addOperation:operation];
   
   
   
   
// 第六种方式 , 子类化 NSOperation ,如果在 PersonOperation 类中没有实现 main 方法,则不会进入到 PersonOperation 类中,因为 main 方法是进入程序的入口
   
PersonOperation *operation = [[ PersonOperation alloc ] init ];
    [queue addOperation:operation];
重点是第三种、第四种,第一种、第二种必须熟练掌握,五六知道即可
6.
UIImageView *imgView2 = [[ UIImageView alloc ] initWithFrame : CGRectMake ( 100 , 250 , 100 , 100 )];
    imgView2.
backgroundColor = [ UIColor orangeColor ];
    [
self . view addSubview :imgView2];
   
NSString *strUrl2 = @"http://img.zjol.com.cn/pic/0/05/50/64/5506405_892666.jpg" ;
   
NSURL *url2 = [ NSURL URLWithString :strUrl2];
//    imgView2.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url2]];
    [imgView2
setImageWithURL :url2];
   
   
UIImageView *imgView3 = [[ UIImageView alloc ] initWithFrame : CGRectMake ( 100 , 400 , 100 , 100 )];
    imgView3.
backgroundColor = [ UIColor orangeColor ];
    [
self . view addSubview :imgView3];
   
NSString *strUrl3 = @"http://pic.paopaoche.net/up/201405/101240_3814389884471.jpg" ;
   
NSURL *url3 = [ NSURL URLWithString :strUrl3];
//    imgView3.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url3]];
    [imgView3
setImageWithURL :url3];


1.GCD支持同步或异步任务处理,串行或并行的处理队列,非系统调用的信号量机制,定时任务处理,进程、文件或网络的监听任务等
2.GCD是一项纯C语言、但又溶有面向对象思想、基于block的多线程编程
3.GCD的优点
     易用:GCD比之thread更简单易用。基于block的特性导致它能极为简单得在不同代码作用域之间传递上下文
     效率:GCD实现功能轻量、优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速
     性能:GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率
     安全:无需加锁或其他同步机制
4.Dispatch Queue是执行处理的等待队列。通过dispatch_async等函数,按照先进先出(FIFO)顺序追加到Queue中处理
     执行处理时,存在两种Dispatch Queue:
          1.Serial Dispatch Queue--等待现在正在执行的任务处理结束(串行)
          2.Concurrent Dispatch Queue--不等待现在正在执行的任务处理结束(并行)
5.Serial Dispatch Queue(也称Private Dispatch Queue)
     一个线程同时执行一个任务,可以避免数据竞争的问题
     可以生成多个Serial Dispatch Queue,各个Serial Dispatch Queue将并行执行
6.Concurrent Dispatch Queue(也称Global Dispatch Queue)
     多个线程同时执行多个任务,效率高。具体多少个线程并发执行,取决于CPU核数和CPU负荷
7.一切跟UI有关的操作必须放在主线程中执行
8.两个经典的死锁
      // 获取系统的主线程队列
        dispatch_queue_t queue = dispatch_get_main_queue();
      // 同步添加
       
// 造成死锁 1
       
// 在主线程队列中添加了一个任务,因为是同步,所以需要等待任务执行完了才能继续往下走
       
// 但是新添加的任务要想执行,由于放在队列的末尾,必须要等到添加的操作执行完了才能继续执行
       
// 因此,程序卡死,不能输出 end
       
NSLog ( @"begin" );
        dispatch_sync(queue, ^{
            NSLog(
@"sync" );
        });
       
NSLog ( @"end" );
       
// 解决方案改为 async
        [[
NSRunLoop currentRunLoop ] run ];
       
       
       
// 死锁 2
       
// 外层 在主线程中添加一个任务,因为是异步添加,所以不需要等待添加任务完成,因此能够输出 end
       
// 里层   用同步添加任务到主线程队列,因此又回到了死锁 1 ,因此程序卡死,不能输出 sync2
       
       
// 结论:永远也不要在主线程中添加事件
       
NSLog ( @"begin" );
        dispatch_async(queue, ^{
            dispatch_sync(queue, ^{
                NSLog(
@"sync2" );
            });
        });
       
NSLog ( @"end" );
       
        [[NSRunLoop currentRunLoop] run];
9. 在UIImageView中扩展一个方法,实现异步加载图片
- ( void )downLoadImage:( NSURL *)url
{
   
   
dispatch_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ), ^{
       
NSData *data = [ NSData dataWithContentsOfURL :url];
       
UIImage *image = [ UIImage imageWithData :data];
       
dispatch_async ( dispatch_get_main_queue (), ^{
           
self . image = image;
        });
    });
}
10.GCD的延迟
//GCD 延迟调用
   
   
// 原理: dispatch_after 并不是在指定时间后执行任务处理,而是指定时间后将任务添加到队列中。所以会有少许的延迟
   
   
// 注意我们不能(直接)取消我们已经提交到 dispatch_after 里的代码
   
   
/*
     void
     dispatch_after(dispatch_time_t when,
     dispatch_queue_t queue,
     dispatch_block_t block);
     */

   
   
//dispatch_time_t->when  什么时候执行延迟调用的代码
   
//dispatch_queue_t -> queue 执行的任务放入到哪个队列
   
//dispatch_block_t -> block 具体执行的任务
   
//    NSLog(@"hoho");
   
   
// 代码执行到此处的时间
    CFAbsoluteTime first = CFAbsoluteTimeGetCurrent();
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(
2 * NSEC_PER_SEC));
   
    dispatch_after(time, dispatch_get_main_queue(), ^{
//        NSLog(@"hehe");
        NSLog(
@"subtime is %f" , CFAbsoluteTimeGetCurrent() - first);
    });
   
11.dispatch_group_async
dispatch_queue_t queue = dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
       
dispatch_group_t group = dispatch_group_create ();
       
       
// 并发任务,执行顺序是随机的
       
dispatch_group_async (group, queue, ^{
           
NSLog ( @"block1" );
//            sleep(4);
        });
       
       
dispatch_group_async (group, queue, ^{
           
NSLog ( @"block2" );
        });
       
       
dispatch_group_async (group, queue, ^{
           
NSLog ( @"block3" );
        });
       
       
/*
        
汇总结果(两种方法二选一)
         */

       
//1. 所有任务执行结束汇总,不阻塞当前线程
//        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//            NSLog(@"group end");
//        });
       
//2. 永久等待,直到所有任务执行结束,中途不能取消,阻塞当前线程
//        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
       
       
//2.1 扩展:等待 1 秒后汇总结果,不管任务有没有执行完
       
dispatch_time_t time = dispatch_time ( DISPATCH_TIME_NOW , ( int64_t )( 1 * NSEC_PER_SEC ));
       
long result = dispatch_group_wait (group, time);
       
if (result == 0 ) {
           
// 任务全部执行完成
           
NSLog ( @" 任务全部执行完成 " );
        }
else {
           
// 还有部分任务没有执行完成
           
NSLog ( @" 还有部分任务没有执行完成 " );
        }
       
NSLog ( @"end" );
       
        [[
NSRunLoop currentRunLoop ] run ];
    }
12.使用Concurrent Dispatch Queue和dispatch_barrier_async函数可实现高效率的数据库访问和文件访问

dispatch_barrier_async的处理流程


13.信号量
dispatch_queue_t queue = dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
       
       
NSMutableArray *marr = [[ NSMutableArray alloc ] init ];
       
       
// 创建一个信号量 , 初始计数为 1, 初始计数的值取决于当前的并发数
       
dispatch_semaphore_t semaphore = dispatch_semaphore_create ( 1 );
       
       
for ( int i = 0 ; i < 100000 ; i++) {
           
dispatch_async (queue, ^{
               
               
//semaphore - 1, 如果 semaphore < 1 则等待,直到 semaphore 的计数值 >=1
               
dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER );
               
                [marr
addObject : @( i ) ];
               
               
// 发信号,使得原来的信号计数值 +1
               
dispatch_semaphore_signal (semaphore);

            });
        }
//        NSLog(@"marr.count is %ld",marr.count);// 在主线程中打印,可能不太准确
       
       
// 使用延迟 , 验证数组中添加了 100000 个数
       
dispatch_after ( dispatch_time ( DISPATCH_TIME_NOW , ( 4 * NSEC_PER_SEC )), queue, ^{
           
           
NSLog ( @"marr.count is %ld" ,marr. count );
           
           
NSSet *set = [ NSSet setWithArray :marr];
           
           
NSLog ( @"set.count is %ld" ,set. count );
        });
       
        [[NSRunLoop currentRunLoop] run];

14.GCD控制多线程并发
/*
        
创建了一个初始值为 3 的信号量,每一次 for 循环都会创建一个新的线程,线程结束的时候会发送一个信号,线程创建之前会信号等待,所以当同时创建了 3 个线程之后, for 循环就会阻塞,等待,有线程结束之后会增加一个信号才能继续执行,如此就形成了对并发的控制
        
         */

       
dispatch_group_t group = dispatch_group_create ();
       
// 创建一个并发数为 3 的线程队列
       
dispatch_semaphore_t semaphore = dispatch_semaphore_create ( 3 );
       
dispatch_queue_t queue = dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
       
for ( int i = 0 ; i < 20 ; i++) {
           
dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER );
           
           
dispatch_group_async (group, queue, ^{
               
NSLog ( @"%d" ,i);
               
sleep ( 2 );
               
dispatch_semaphore_signal (semaphore);
            });
        }
       
dispatch_group_wait (group, DISPATCH_TIME_FOREVER );
        NSLog(@"end");



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值