iOS Block 概览

238 阅读2分钟

如何定义一个block

^ [返回值类型][参数列表][表达式]

列: ^int (int count) { return count + 1}

返回值为空时可以省略: ^ [void][参数列表][表达式] = ^ [参数列表][表达式]

参数为空时也可以省略: ^ [返回值类型][void][表达式] = ^ [返回值类型][表达式]

如何声明一个block

[返回值类型][^block名称][参数列表]

例:int (^name)(int)

也可以用 typedef 去定义一个block类型,比如:

typedef int (^Test)(int);
Test test = ^ (int num) {
// Implement
};

Block 中的截获参数

对于传入的参数:

void testStringIn() {
    NSString *s = @“start”;
    
    void (^S)(NSString *) = ^(NSString *string) {
		  NSLog(@“%@“, string); // 2
        string = @“test”;
        NSLog(@“%@“, string); // 3
    };
    
	  s = @“end”;
    NSLog(@“%@“, s); // 1
    S(s);
}

// end
// start
// test

在block中修改后不会影响外面的值, 且会持有在block之前声明的值,不会因为修改而改变。

对于捕获到的参数,若果需要在block中修改,需要加上__block表示符: 但是如果是静态变量、静态全局变量、全局变量则不需要添加。


void testStringOut() {
    __block NSString *s = @“start”;
    
    void (^so)(void) = ^ {
        s = @“test”;
        NSLog(@“%@“, s);
    };
    
    NSLog(@“%@“, s);
    so();
    NSLog(@“%@“, s);
}
// start
// test
// test

Block类型

主要分为三种Block:

  1. NSConcreteGlobalBlock 存储域:程序的数据区域

特征:

全局变量、不使用自动变量。

相当于单例使用,不被系统回收。

不会持有对象。

  1. NSConcreteStackBlock:栈

不会对参数进行持有。

  1. NSConcreteMallocBlock:堆

会对参数进行引用计数,不管是 __block还是普通参数。

NSConcreteStackBlock很少在arc中见到,因为在初始化时调用objc_retainBlock->_Block_copy->_Block_copy_internal方法链会导致转为NSConcreteMallocBlock类型。

也可以通过不赋值的方式声明:

int main(int argc, const char * argv[]) {
    
    __block int temp = 10;
    
    NSLog(@“%@“,^{NSLog(@“*******%d %p”,temp ++,&temp);});
   
    return 0;
}
// <__NSStackBlock__: 0x7fff5fbff768>

__block

在block中,也能对参数进行修改比如:

    NSMutableString *s = [NSMutableString stringWithString:@“abc”];
    
    void (^so)(void) = ^ {
        [s appendString:@“def”];
        NSLog(@“%@“, s);
    };

对字符串进行修改,或者在数组中添加参数,但是如果要对传入的指针赋值,则只能在变量前加上__block标识。

在加入__block之后会转为结构体的形式:

__block int i = 0; //转化

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;
 int __flags;
 int __size;
 int I;
};

__Block_byref_i_0,这个结构体有5个成员变量。 第一个是isa指针, 第二个是指向自身类型的__forwarding指针, 第三个是一个标记flag, 第四个是它的大小, 第五个是变量值,名字和变量名同名.

__forwarding指针初始化传递的是自己的地址,但是在block拷贝之后,指向的则是block中的自己,这样就能通过i->__forwarding->i)来访问到变量值。