Block是什么(进阶-种类篇)
前言
- 上一章我们了解了Block的基本使用和简单用法,这一章我们来进阶一下Block的种类和Block使用会遇到的问题。
Block的种类
- 栈区Block(NSStackBlock)
- 定义:在ARC下,捕获了其他作用域变量的Block在创建时(++未进行赋值、未作为方法返回值、未进行copy操作++)都是存在于栈区的Block,在离开相应的作用域后,栈内存会自动管理其内存释放。
- 举个栗子(请务必自己敲出来看下输出)
这个例子就是打印了一个Stack的Block.这种Block的内存管理由栈来管理,未进行引用计数,因此也不用处理循环引用问题NSLog(@"%@",^{ NSLog(@"%d",self.g); });
- 总结:在ARC下,栈Block中我们一般无须处理循环引用问题。
- 堆区Block(NSMallocBlock)
- 定义:在ARC下,栈区的Block经过赋值、作为返回参数、进行copy操作等会让Block由栈内存拷贝到堆内存。
- 举个例子(请务必自己敲出来看下输出)
这个例子就是++ARC下++直接将我们上发的Block进行了copy操作后,就将栈区的Block转向了堆区。NSLog(@"%@",[^{ NSLog(@"%d",self.g); //self.g是随便定义的实例变量~ } copy]); //这里打印出来就是一个NSMallocBlock void (^myblock)()= ^{NSLog(@"%f",self.g);}; //这里就是将右边的块赋值给myblock的堆区Block,注意,左边的myblock 才是堆区的block,右边的匿名block本身还是一个栈区的block.
- 总结:ARC下,block从栈复制到堆区,就开始了引用计数,所以我们需要处理堆内存内的对象一样处理NSMallocBlock;++因此,我们需要处理堆block内的循环引用问题++
- 全局Block(NSGlobalBlock)
- 定义: 在ARC下,没有捕获变量或者捕获的变量是全局性的(staic,const修饰的变量),block的代码结果已经在编译时可确定,这种block就是一个全局(global)Block.
- 举个栗子:
这里的myblock,在打印出来后,就可以看到是一个void (^myblock)(); myblock=^{ NSLog(@"44444"); }; NSLog(@"%@",block);
test[72638:5520226] <__NSGlobalBlock__: 0x10df6e010>
- 总结: NSGlobalBlock.这种Block存在于++代码区的内存中++,在第一次访问block的时候,就会缓存起来,且全局的实例只会有一个,类似于单例,其copy操作是一个空方法,只会返回其本身的指针。这种Block没有捕获外部的可变变量,++即不需要处理其循环引用.++
- 总结:
- 需要处理循环引用的只有堆区的block,其他两种都不需要处理循环引用。
- 在ARC下,栈区的Block不会++显示++存在,但是大部分系统自带的block遍历方法,都用到了。我们在封装自己的方法时,尽量使用stackblock,这样就不需要太去注意循环引用的释放问题,但是,不可太滥用stackBlock,因为其处于栈区内存,系统可能会去释放覆盖掉其内存。
- 典型的StackBlock在Masnory 和AFNet中普遍存在,有兴趣了解的,可以读其源码,观察Block是如何传递的.
- 在平时代码习惯中,一定记住去处理堆区Block,否则会造成严重的内存泄露。
后语
笔者接触iOS开发不算太久至今6个月,不过喜欢上了OC的这门动态语言,有失误见解的地方,请指出,提出你的意见。下一章,会讨论一下Block的对象性。