iOS学习之block

  • block简介:
    我们可以把Block当做Objective-C的匿名函数。Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观。另外,block的实现具有封闭性(closure),而又能够很容易获取上下文的相关状态信息。

  • block创建:

    block使用了与函数相同的机制:可以像声明函数一样,来声明一个block 变量;可以利用定义一个函数的方法来定义一个block;也可以将block当做一个函数来调用。

 @autoreleasepool {
        //block 块定义 带参
        double (^distanceFromRateAndTime)(double rate,double time);

        //block块声明
        distanceFromRateAndTime=^double(double rate,double time)
        {
            return rate*time;
        };
        // block块调用
        double distance = distanceFromRateAndTime(15,3);
//        不代参block块定义
        double (^random)();
        random=^double{
            return arc4random()%100;
        };
//        ====
//        等价于下面的 定义与声明在一起写
        double (^randdom)()=^double{
            return arc4random()%100;

        };
        random();
    }
  • block的闭包性:

在block内部,可以像普通函数一样访问数据:局部变量、传递给block的参数,全局变量/函数。并且由于block具有闭包性,所以还能访问非局部变量(non-local variable)。非局部变量定义在block之外,但是在block内部有它的作用域。

       NSString * happyday=@"new week";
        NSString *(^happyWeek)(NSString *param)=^NSString*(NSString *param)
        {
            return  [happyday stringByAppendingString:param];
        };
      NSLog(@"%@",  happyWeek(@"keep moving"));//new weekkeep moving

非局部变量会以const变量被拷贝并存储到block中,也就是说block对其是只读的。如果尝试在block内部给make变量赋值,会抛出编译器错误。
这里写图片描述
以const拷贝的方式访问非局部变量,意味着block实际上并不是真正的访问了非局部变量——只不过在block中创建了非局部变量的一个快照。当定义block时,无论非局部变量的值是什么,都将被冻结,并且block会一直使用这个值,即使在之后的代码中修改了非局部变量的值。如下:

  NSString * happyday=@"new week";
        NSString *(^happyWeek)(NSString *param)=^NSString*(NSString *param)
        {
            return  [happyday stringByAppendingString:param];
        };
          NSLog(@"%@",  happyWeek(@"keep moving"));//new weekkeep moving
        happyday=@"new day”;//修改对象的值
         NSLog(@"%@",  happyWeek(@"keep moving"));//new weekkeep moving
  • 修改非局部变量:

冻结中的非局部变量是一个常量值,这也是一种默认的安全行为——因为这可以防止在block中的代码对非局部变量做了意外的修改。那么如果我们希望在block中对非局部变量值进行修改要如何做呢——用__block存储修饰符(storage modifier)来声明非局部变量:
__block NSString * happyday=@”new week”;
这将告诉block对非局部变量做引用处理,在block外部make变量和内部的make变量创建一个直接的链接(direct link)。现在就可以在block外部修改make,然后反应到block内部,反过来,也是一样。
这里写图片描述

对于用__block修饰的外部变量引用,block是复制其引用地址来实现访问的
第二种方式以:用static去修饰变量

          static int photo=1;
        void(^modify)(void)=^{
            photo++;
            NSLog(@"modify %d",photo); 
        }; 
  • Block作为函数的参数:

把block存储在变量中有时候非常有用,比如将其用作函数的参数。这可以解决类似函数指针能解决的问题,不过我们也可以定义内联的block,这样代码更加易读。如:
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
- 定义Block类型:

typedef <#returnType#>(^<#name#>)(<#arguments#>);
由于block数据类型的语法会很快把函数的声明搞得难以阅读,所以经常使用typedef对block的签名(signature)做处理。
代码示例:

      /**
         *  不带block修饰符
         100 101 102 103 104
         105 106 107 108     109
         110 111 112 113 114
         115 116 117 118 119
         */
        int num=100;
        for (; num<120; num++) {
            increate block=^{

                printf("%d \t",num);
            };
            block();
        }
        /**
         *  带block修改符
         200 201 202 203 204
         205 206 207 208 209
         210 211 212 213 214
         215 216 217 218 219
         */
        __block  int count=200;
        for (; count<220; count++) {
            increate block=^{

                printf("%d \t",count);
            };
            block();
        }
  /*************华丽丽的分隔线***************/
    /**
         *  不带block修饰符,在异步线程里使用
         100 101 102 103 105
         104 106 107 108 109
         110 111 112 113 114
         115 116 117 118 119
         */
        int number=100;
        for (; number<120; number++) {
            increate block=^{

                printf("%d \t",number);
            };
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
        }
        /**
         *  带block修饰符 在异步线程里使用
         111 120 120 120 120
         120 120 120 120 120
         120 120 120 120 120
         120 120 120 120 120
         结果为什么是这样的
         */
        __block  int number=100;
        for (; number<120; number++) {
            increate block=^{

                printf("%d \t",number); 、、
            };
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
        }

block块数组:
这里p的类型为void(^[2])(void),表示含有2个void(^)(void)块引用元素的变量。

void(^p[2])()= { ^(void){ puts("Hello, world!"); }, ^(void){ puts("Goodbye!"); } };
        p[0]();
        p[1]();
  • block和函数指针有什么区别?

    第一个区别,函数指针是对一个函数地址的引用,这个函数在编译的时候就已经确定了。
    而block是一个函数对象,是在程序运行过程中产生的。在一个 作用域中生成的block对象分配在栈(stack)上,和其他所有分配在栈上的对象一样,离开这个作用域,就不存在了.
    用block实习递归
    我们也可以引用 static block 或 __block block。比如我们可以用他们来实现 block 递归:

      static int (^ const staticBlock)(int )=^int (int i)
        {
            return i == 1 ? 1 :( i*  staticBlock(i-1));
        };
        NSLog(@"%d",staticBlock(3));
        __block  int(^blockBlock)(int)=^int(int i)
        {
            return i == 1 ? 1 :( i*  blockBlock(i-1));
        };
        NSLog(@"%d",blockBlock(3));
  • block的循环引用问题

当一个Block被Copy的时候,如果你在Block里进行了一些调用,那么将会有一个强引用指向这些调用方法的调用者,有两个规则:
如果你是通过引用来访问一个实例变量,那么将强引用至self
如果你是通过值来访问一个实例变量,那么将直接强引用至这个“值”变量
苹果官方文档里有两个例子来说明这两种情况:
这里写图片描述
上面第一种情况相当于用self.xxx来访问实例变量,所以强引用指向了self;第二种情况把实例变量变成了本地临时变量,强引用将直接指向这个本地的临时变量。大多数情况下,我们只用处理第一种情况就行了,因为第二种情况虽然会造成循环引用,但是临时变量很快就被释放了,不会造成真正的循环引用。要避免强引用到self的话,用__weak把self重新引用一下就行了,像这样:

__weak ViewController *weakSelf = self;  

欢迎拍砖!

参考博客:http://www.cocoachina.com/ios/20100429/1276.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值