如何定义一个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:
- NSConcreteGlobalBlock 存储域:程序的数据区域
特征:
全局变量、不使用自动变量。
相当于单例使用,不被系统回收。
不会持有对象。
- NSConcreteStackBlock:栈
不会对参数进行持有。
- 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)来访问到变量值。