简介
block是在iOS 4.0之后才出现的,它可以将一段代码封装起来作为变量使用。
block声明:
返回值 (^block名称) (参数列表…)
如: void (^blockName)(int arg1, int arg2)
block定义(实现):
blockname = ^(int arg1, int arg2){
//do something
}
如下:
使用
block的作用类似java中接口回调,但仅仅是作用类似,原理上完全是两个东西。
举例:作为参数回调使用:
block还能实现链式调用,返回一个block对象,并在block中返回自身对象。
注意:
1、block的内存中有一块是专门来捕获变量的,在声明的block的范围内,所有变量都可以被其捕获。但是对外部变量只能读,如果想要修改,需要给变量加上block关键字修饰。
2、避免循环引用, 使用weak关键字来修饰需要引用的外部对象,防止导致内存泄漏。
3、可以利用typedef对block进行别称定义,减少代码量,增加可读性,将“对象抽象成类”。比如:typedef void(^SayHello)();
block深究
1、block是一个函数对象,是在运行时产生的,在一个作用域中生成的block对象分配在栈上,离开作用域,就不存在了(可以利用copy将block对象拷贝到堆上)。
每个对象都会有一个isa指针,那么根据isa指针类型,block有三种:
全局静态(globalBlock)/保存在程序的数据区域中(.data区)
出作用域销毁(stackBlock)/保存在栈中,
retainCount=0销毁(mallocBlock)/保存在堆中
2、block访问普通外部变量时,会将变量的值以“const方式”copy一份到block所在内存空间,所以block内部访问的并不是真正的外部变量,而且因为是const方式,所以编译器不允许修改copy的变量。
输出:
block外部访问:value = 0
block外部访问:value的地址是:0x7ffeea11a0c4
block内部访问:value = 0
block内部访问:value的地址是:0x600003cc10a0
block回到外部访问:value的地址是:0x7ffeea11a0c4
还有,要注意一点:若将变量定义为__block形式,那么变量的地址将会在block结束后改为block中copy生成的新地址。
3、其实由上可以看出,在block中引用对象,会导致对象的生命周期被延长,特别是当某些大文件被block访问时,有几率会导致内存访问不足。
4、利用block做回调时,在避免循环引用的同时,还要防止引用的对象被提前释放,从而可能会因为操作nil对象导致crash,或者执行一些无意义的逻辑。所以需要我们在block内部操作时加上保护代码。(这种场景在网络访问的情景下较为常见)。
未完待续。。。