block
1.定义
:block是C语言的扩充,其功能是带有自动变量(局部变量)的匿名函数(不带有名称的函数)。block提供了类似C++和Object- C类生成实例或对象来保持变量的方法。
2.语法:^(返回值类型)(参数列表){表达式};返回值类型可以省略,此时若表达式中有return语句,就使用return的类型,若没有就返回void。表达式中含有多个return语句时,所有return的返回值类型必须相同。若没有参数,参数列表也可以省略。
3.block类型变量:int (^blk)(int) 。变量名为blk,可以通过blk进行变量间赋值。可将block语法赋值给声明为block类型的变量中:
int (^blk)(int) = ^(int count){return count+1;};block类型的变量和C语言中变量完全相同。
在函数参数或返回值中使用block类型变量时,可以使用typedef来解决记述复杂的问题:typedef int (^blk_t)(int);blk_t就可以作为block类型的别名来使用。
4.截获自动变量:block可以使用在block定义之前申明的变量,并且可以保存block里用到的自动变量值。但是在block之后,对该自动变量值进行修改,是不会在block中体现出来的,block中的自动变量始终是最开始的值。
5.使用附有__block说明符的自动变量可在block中赋值。
6.赋值给截获的oc对象会产生编译错误,但是使用截获的对象不会有任何问题,如在block前定义了可变数组array,在block中对array添加元素是可以的,但是对array附新的对象值是不可以的。
7.在使用C语言数组时必须小心使用其指针。即要使用c语言的数组,最好用指针来声明。这是因为在现在的blocks中,截获自动变量的方法并没有实现对C语言数组的截获,使用指针就可以解决这个问题。
9.截获自动变量值:意味着在执行block语法时,block语法表达式所使用的自动变量值被保存在block的结构体实例中。
10.block只截获自动变量的值,但是在block中改变静态变量、静态全局变量、全局变量是完全没有问题的。但是静态变量与其他两个全局变量还有不同。全局变量的改变没有任何改变,也不需要在block结构体中增加相应的成员变量。但是对于静态变量,同自动变量一样,都会在block结构体中增加相应的成员变量,与自动变量所不同的是,增加的是指向静态变量的指针。将静态变量的指针传递给_main_block_impl_0结构体的构造函数并保存。这是超出作用域使用变量的最简单方法。
11.__block说明符是存储域类说明符,他类似于C语言中的static,auto和register说明符,他们用于指定将变量设置到那个存储域中。使用__block说明符声明变量相当于产生了一个__Block_byref_val_0结构体类型的自动变量。
12.Block可以转换为Block的结构体类型(_main_block_impl_0)的自动变量。所谓结构体类型的自动变量,即栈上生成的该结构体的实例。__block变量是栈上__block变量的结构体实例。
13.Block是oc的对象,将其看作对象时,该Block的类为_NSConcreteStackBlock。_NSConcreteStackBlock:该类的对象Block设置在栈上。_NSConcreteGlobalBlock类对象设置在程序的数据区域。_NSConcreteMallocBlock对象设置在堆上。
14.需要截获自动变量的Block为_NSConcreteStackBlock类,但是当记述全局变量的地方有Block语法时,或者Block语法的表达式中不使用应截获的自动变量时,Block为_NSConcreteGlobalBlock类对象。
15.当ARC有效时,大多数情况下编译器会恰当的进行判断,自动生成将Block从栈上复制到堆上的代码。将Block作为函数返回值返回时,编译器会自动生成复制到堆上的代码。
16.手动复制栈上对象到堆上可以使用copy。对于已经配置在堆上的Block,copy方法使其引用计数加一。已经配置在程序的数据区域上的Block,copy方法什么都不做。
17.栈上的_block变量用结构体实例在__block变量从栈复制到堆上时,会将成员变量_forwarding的值替换为复制目标堆上的_block变量用结构体实例的地址。
18.以下情况,栈上的Block会复制到堆上:
(1)调用Block的copy实例方法时。
(2)将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时。(因为类必须都是存在于堆上的,所以当Block赋值给类,或者类的成员变量时,要将其复制到堆上)
(3)Block作为函数返回值返回时(需要在变量作用域外调用,所以必须要复制到堆上,以防局部变量被销毁,对象失效)。
(4)在方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时。
19.定义__block说明符变量或者使用copy方法将Block从栈复制到堆上,都可以实现Block中截获的对象能够超出其变量作用域而存在。这两者的本质都是通过Block变量用结构体的成员变量存储截获的对象,并将其从栈上复制到堆中。
20.Block中使用对象类型自动变量时,除了编译器能自动添加复制代码的情况外,推荐调用Block的copy实例方法。
21.如果在Block中使用附有_strong修饰符的对象类型自动变量,那么当Block从栈复制到堆时,该对象为Block所有。而若此Block正好又是该类的成员变量,就会导致循环引用。此时对象持有Block,Block持有对象。(将Block赋值给block类型成员变量时,编译器会自动将block从栈复制到堆中)。编译器可以检测这个循环引用,并发出警告。
22.Block循环引用的避免:(1)使用_weak修饰符的变量,并将block中使用到的关于该类的变量赋值使用。
(2)使用_block变量来避免:将Block中用到的关于类的变量赋值给_block类型的变量,并在Block内最后将其置为nil,并且必须执行该Block。若不执行该Block,则循环引用的关系一直存在。执行Block后,在Block内_block类型的变量置为nil,释放了持有的对象,从而打破了循环引用的关系。
23.堆上的的Block可以通过retain方法持有,但是对于配置在栈上的Block调用retain实例方法则不起任何作用。
24.在ARC无效时,_block变量类似于在ARC有效时_unsafe_unretained一样的方法来使用。所以ARC有效和无效时,_block说明符的用途有很大的区别。ARC无效时,只能通过_block来避免循环引用。因为当Block从栈复制到堆上时,若Block使用的变量为附有_block说明符的id类型或对象类型自动变量,不会被retain,若不是_block类型变量,则会被retain。