运行结果
分析
方法内部的obj变量在栈中,变量内存地址0x7fff543d0c98,所指向的对象<nsobject: 0x7fcb1bd22390>在堆中,内存地址0x7fcb1bd22390,它的引用计数+1。
<code>block中obj指针已经不是外部的obj指针了,它是外部变量obj的拷贝</code>,它的内存地址是0x7fcb1c903fb0,跟外部obj不一样,但是所指向的对象也是0x7fcb1bd22390,0x7fcb1bd22390对象引用计数再+1=2。
block中的obj指针(内存地址是0x7fcb1c903fb0)对对象(<nsobject: 0x7fcb1bd22390>)的引用是强引用,在外部将obj(地址0x7fff543d0c98)置为nil后,外部的obj不再指向<nsobject: 0x7fcb1bd22390>对象,0x7fcb1bd22390对象的引用计数-1,引用计数为1,<code>0x7fcb1bd22390对象内存不被自动回收</code>,所以第二次调用block,0x7fcb1bd22390对象还在内存中。
结论
block内部的obj 指针是外部obj指针的拷贝,有2个指针指向同一个nsobject对象,但只将外部的obj指针置为nil,nsobject对象的引用计数不为0,无法回收。
分析 :
方法内部的weakobj变量在栈中,变量内存地址0x7fff543d0c98,所指向的对象<nsobject: 0x7fcb1bd2fee0>在堆中,内存地址0x7fcb1bd2fee0,它的引用计数+1。
weakobj变量也在栈中,内存为0x7fff543d0c90,所指向的对象也是<nsobject: 0x7fcb1bd2fee0>,弱引用,所以0x7fcb1bd2fee0对象的引用计数不增加,仍然为1.
block中weakobj它的内存地址是0x7fcb1bc072b0,跟外部的weakobj不同,但是所指向的对象也是0x7fcb1bd2fee0,弱引用,0x7fcb1bd2fee0对象引用计数还是不增加,仍然是1。
-在外部将obj(地址0x7fff543d0c98)置为nil后,外部的obj不再指向<nsobject: 0x7fcb1bd2fee0>对象,0x7fcb1bd2fee0对象的引用计数-1,引用计数为0,arc回收0x7fcb1bd2fee0对象内存,并将指向它的弱引用指针赋值为nil,所以第二次调用block,0x7fcb1bd2fee0对象不在在内存中。
在block中__weak声明的指针去引用对象 可以避免循环引用的问题,但是当外部对象被释放了,block 内部会访问不到这个对象. 这种问题如何解决呢?先来看一段代码:
执行结果如下:
总结 :
多线程的时候,在 <code>block 外部</code>用<code>__weak</code>声明的变量指向一个对象, 通过把weak声明的变量值赋值给<code>block内部</code>的```strong<code>变量</code>,实现在block内对该对象进行强引用,这样可以在block生命周期内保留该对象不被释放,在block生命周期结束后,对象内存被释放。
第3处日志打印了一个testblock对象,blockobj的地址发生变化。此时,<code>block对象</code> 从栈拷贝到堆上,__block变量blockobj,也被拷贝到堆上。block对象<code>拥有</code>blockobj指针指向的对象。注意:这是个强引用哦。
关注4到8 处日志,用__block关键字声明blockobj指针后,block内外的变量blockobj都是0x7fa084905838,也就是block内外的blockobj指针是同一个指针。
block内部改变 blockobj指针指向的对象,改动在 block外部可见。
<a href="https://www.zybuluo.com/microcai/note/51116" target="_blank">block实现原理(一)</a>
<a href="https://www.zybuluo.com/microcai/note/57603" target="_blank">block和变量的内存管理(二)</a>
在oc,在block中直接访问外部变量,访问的是外部变量的copy。用clang后将 .m翻译为.cpp文件后发现,外部函数是通过<code>传值方式</code>将变量值传给block(block结构体、block最终要执行的函数代码).
使用了__block后,外部函数是通过<code>指针传递</code>,将变量传递到 block 内,所以可以修改变量值.
block作为c语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事。需要注意的是由于objective-c在ios中不支持gc机制,使用block必须自己管理内存,而内存管理正是使用block坑最多的地方,错误的内存管理 要么导致return cycle内存泄漏要么内存被提前释放导致crash。 block的使用很像函数指针,不过与函数最大的不同是:block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,block不仅 实现函数的功能,还能携带函数的执行环境。
可以这样理解,block其实包含两个部分内容:
block执行的代码,这是在编译的时候已经生成好的;
一个包含block执行时需要的所有外部变量值的数据结构。 block将使用到的、作用域附近的变量建立一份快照拷贝
根据block在内存中的位置分为三种类型:_nsconcreteglobalblock,_nsconcretemallocblock, _nsconcretestackblock。
<code>_nsconcreteglobalblock</code>:类似函数,位于text段;
<code>_nsconcretestackblock</code>:位于栈内存,函数返回后block将无效;
<code>_nsconcretemallocblock</code>:位于堆内存。
blocks that don't capture any variables are global blocks. since all instances of the block are the same, the compiler can just allocate one copy statically for the life of the program。
blocks that capture variables (closures) are either stack or heap (malloc) blocks. blocks start out on the stack, as stack blocks. when a stack block is copied for the first time, it is moved to the heap. copying a heap block does not create another copy; but simply retains it.
在开启 arc 时,大部分情况下编译器通常会将创建在栈上的 block 自动拷贝到堆上。
当 block 作为函数返回值返回时,编译器自动将 block 作为 _block_copy 函数,效果等同于 block 直接调用 copy 方法;
当 block 被赋值给 __strong id 类型的对象或 block 的成员变量时,编译器自动将 block 作为 _block_copy 函数,效果等同于 block 直接调用 copy 方法;
当 block 作为参数被传入方法名带有 usingblock 的 cocoa framework 方法或 gcd 的 api 时。这些方法会在内部对传递进来的 block 调用 copy 或 _block_copy 拷贝;
在没有arc之前,由于arc 自动将栈中block拷贝到堆上,所以当returnblock函数退出,在栈中内存释放后,仍然可以访问到block对象。
在以下情形中, block 会从栈拷贝到堆:
当 block 调用 copy 方法时,如果 block 在栈上,会被拷贝到堆上;
当 block 作为参数被传入方法名带有 usingblock 的 cocoa framework 方法或 gcd 的 api 时。这些方法会在内部对传递进来的 block 调用 copy 或 _block_copy 进行拷贝;
其他情况需要手动拷贝。
程序会报exc_bad_access ,getblockarray返回的数组里面的 block 是不可访问的。
手动copy后,block拷贝到堆上,getblockarray函数返回的栈帧被销毁后,仍可以访问堆中的block拷贝。
推荐文章:
<a href="http://www.tanhao.me/pieces/310.html/" target="_blank">http://www.tanhao.me/pieces/310.html/</a>
<a href="http://www.tanhao.me/pieces/310.html/" target="_blank">block 内存管理</a>
<a href="http://devtian.me/2015/05/10/ios-block-1/" target="_blank">block 实现原理</a>
<a href="http://clang.llvm.org/docs/block-abi-apple.html" target="_blank">block-abi-apple</a>
<a href="http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/" target="_blank">正确使用block避免cycle retain和crash</a>
<a href="http://honglu.me/2015/01/06/weak%e4%b8%8eblock%e5%8c%ba%e5%88%ab/" target="_blank">weak与block区别</a>
<a href="http://cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html" target="_blank">how blocks are implemented (and the consequences</a>
<a href="http://clang.llvm.org/docs/block-abi-apple.html" target="_blank">block实现原理</a>