block 对外部引用变量的处理
MRC 环境
一、静态变量 和 全局变量 在加和不加 __block 都会直接引用变量地址。也就意味着 可以修改变量的值。在没有加__block 参数的情况下。
- 全局block 和 栈block 区别为 是否引用了外部变量,堆block 则是对栈block copy 得来。对全局block copy 不会有任何作用,返回的依然是全局block。
二, 常量变量(NSString *a = @"hello";a 为常量变量,@“hello”为常量。)-----不加__block类型 block 会引用常量的地址(浅拷贝)。加__block类型 block会去引用常量变量(如:a变量,a = @"abc".可以任意修改a 指向的内容。)的地址。
如果不加__block 直接在block 内部修改变量 ,会编译报错。block 内部改变量是 只读的。
但是 就一定可以推断 block 会深拷贝 该变量吗???
对于常量 @“hello” 存储在 内存中的常量区, 程序结束才会释放 内存。 如:
NSString *str = @"hello";
NSString *abcStr = @"hello";
编译器会优化处理, str 和 abcStr 都会指向 常量区的@“hello” 地址。
NSString *str = @"hello";
void (^print)(void) = ^{
NSLog(@"block=str======%p",str);
}
str = @"hello1";
print();
block 会拷贝变量内容到自己的栈内存上,以便执行时可以调用。 但并不是对str 内容做了深拷贝,重新申请内存。
因为str 是栈内存上的变量,指向 一个常量区的@“hello”. 编译器做的优化是 当block 去拷贝str 指向内容时发现是个常量,
所以会去引用 @“hello” 的指针,没必要再取申请一块内存。
三、对象变量 如(MyClass *class、Block block)。 这里block 也是”类“对象(类似对象,其包含isa指针,clang 反编译可以查看。因为它不像从NSObject 继承下来的对象都支持 retain、copy、release)。
下面直接引用文章里总结的,经验证无误。
Block的copy、retain、release操作
不同于NSObjec的copy、retain、release操作:
- Block_copy与copy等效,Block_release与release等效;
- 对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
- NSGlobalBlock:retain、copy、release操作都无效;
- NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[
[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。 - NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
- 尽量不要对Block使用retain操作。
文章链接:http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/
以下补充
栈block : 猜测》》》》会copy 内部引用的对象变量。(如何验证 block copy 了外部变量......在block 执行前 释放对象.)。
但实际对两种对象变量的操作为:
- MyClass *class : 栈block 并不会copy 对象变量,也不会retain 对象。而是直接引用了对象变量的地址。可以在blcok 执行前释放对象验证。(有点毁三观啊)
- Block block ( ”类“对象):不会对 block 做处理。如果block 是栈block ,执行时依然为栈block. 堆block 同理
堆block : 通过copy 栈block 获得, 当向栈block copy 时。会对内部引用的对象变量如下处理。
- MyClass *class : block 会retain 内部引用的 对象变量,改变引用对象的内存计数。
- Block block( ”类“对象): ”类“对象block 执行copy ,如果是栈block。如果为堆block 并不会对他copy 。
GCD block :会对内部引用的对象变量如下处理。
- MyClass *class: retain 内部引用的对象变量,改变引用对象的内存计数。
- Block block( ”类“对象): ”类“对象block 执行copy 。
dispatch_async(dispatch_get_main_queue(), ^{
});
当然如果引用了外部栈block 变量,也会copy 栈block 到堆上。同时栈block 对其内部引用对象变量 重复前面的操作。
这里不如说 GCD 里的 block是内部做了处理的堆block 。
以上结论均在 MRC 环境 的 viewDidLoad 方法中测试。
ARC环境下
虽然 ARC 环境下会对 栈block 做优化,当创建一个栈block 时默认返回一个 堆block 。但 并不是ARC 环境下没有栈block 。
如
-(void)demo:(Block)blocka;
-(void)demo:(Block)blocka{
NSLog(@"==demo=====%@",blocka);
}
通过 方法参数传递的 block 就是 栈block。
由此引出 block 循环引用问题:
循环引用 成立的条件: 直接导致A B 两个对象相互强引用(retain,strong)、或者 间接导致 A B 两个对象相互强引用。
直接循环引用:
ARC 环境。


ARC 中 LLVM 会监控对象引用情况,如果出现循环引用会waring.
第一个警告是间接循环引用。
- ViewController -----retain--> Custom
- Custom -------强引用--->CustomBlock(堆block,会retain 内部引用对象变量)
- CustomBlock ---retain--------->self(ViewController)
第二个警告 直接循环引用。
- ViewController --强引用------>Block(堆block,会retain 内部引用对象变量)
- Block------retain--------->self(ViewController)
对于第三个 我们常用的GCD block 中,则没有出现警告。虽然 gcd block 同样对self 进行了retain.
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"====%@",self);
});
但是self 并没有直接或间接的去强引用 gcd block。 可以想象 gcd block 会统一被管理在GcdBlockDispatchCenter(这个是我瞎扯的,相当于GCD block 调度中心 来管理,反正不是self 。)。
还有一种情况下不会出现循环引用 如: ARC 环境



2014-12-30 11:05:59.119 Test_Class[4235:230795] ==demo=====<__NSStackBlock__: 0x118600>
上面说到,栈block 并不会对内部引用对象变量 retain .而是直接引用对象变量地址。、、
- 这里 self 和 Custom 并没有对 方法参数blocka 做任何引用操作,blocka 一直存在于栈上,直到执行完毕被释放掉。
- 当然blocka 也没有对self 做任何操作,直接引用了地址。
以上 也就是说只有堆block 才会存在循环引用的情况。
》》待续.....
复习下内存分类: http://blog.csdn.net/u011848617/article/details/39346075
block 对外部引用变量的处理的更多相关文章
- 环境变量、block、修饰符:block对环境变量的引用和修改需要通过修饰符来限定
环境变量.block.修饰符:block对环境变量的引用和修改需要通过修饰符来限定. http://www.cnblogs.com/fengmin/p/5816580.html - (NSUInteg ...
- block循环引用
block里边会有循环引用的风险,它可能对外部一个变量出现强引用,所以需要判断里边是否有循环引用,通过dealloc方法(销毁当前控制器.或销毁要测试的变量),判断是否循环引用.主要在block 里边 ...
- ios - block循环引用Demo示例
当实例变量中有了block属性,并且用copy来修饰,但是当调用block中的代码的时候,如果block中运用了self.属性的时候回造成循环引用. // // ViewController.h // ...
- iOS Block循环引用
在介绍block循环引用前我们先了解一下typeof. typeof是什么??? typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型. 它返回值是一个字符串,该字符串说明运算数的类 ...
- Block循环引用问题研究
自从苹果在objc中添加Block功能支持以后已经过了很久.目前网上对于Block的使用有很多介绍.不过对于Block的内存管理问题,则是众说纷纭.再加上objc开始使用ARC以后,对于Block的内 ...
- IOS block 循环引用的解决
在介绍block循环引用前我们先了解一下typeof. typeof是什么??? typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型. 它返回值是一个字符串,该字符串说明运算数的类 ...
- block捕获自动变量和对象
一.捕获自动变量值 首先看一个经典block面试题: int val = 10; void (^blk)(void) = ^{printf("val=%d\n",val);}; v ...
- Block的引用循环问题 (ARC & non-ARC)
2010年WWDC发布iOS4时Apple对Objective-C进行了一次重要的升级:支持Block.说到底这东西就是闭包,其他高级语音例如Java和C++已有支持,第一次使用Block感觉满简单好 ...
- 深入研究Block捕获外部变量和__block实现原理
Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”.从那开始,Block就出现在iOS和Mac系统各个API中,并被大 ...
随机推荐
- BZOJ1264 [AHOI2006]基因匹配Match 动态规划 树状数组
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1264 题意概括 给出两个长度为5*n的序列,每个序列中,有1~n各5个. 求其最长公共子序列长度. ...
- <构建之法>阅读笔记6
第九章:项目经理 是讲项目经理的作用功能和重要性,书里面主要讲的是微软的PM(Programe Manager)和其他团队PM(Project Manager)的区别,还介绍了PM的能力要求以及人物, ...
- 二分搜索-poj2785
题目链接:http://poj.org/problem?id=2785 题目大意:要求输入A,B,C,D四个数组,从每个数组中分别取出一个数来相加,求出相加后 和为0 总共有多少种加法. #inclu ...
- tensorflow入门(1):构造线性回归模型
今天让我们一起来学习如何用TF实现线性回归模型.所谓线性回归模型就是y = W * x + b的形式的表达式拟合的模型. 我们先假设一条直线为 y = 0.1x + 0.3,即W = 0.1,b = ...
- php手撸轻量级开发(一)
聊聊本文内容 之前讲过php简单的内容,但是原生永远是不够看的,这次用框架做一些功能性的事情. 但是公司用自己的框架不能拿出来,用了用一些流行的框架比如tp,larveral之类的感觉太重,CI也不顺 ...
- JAVA编码 —— 字符串关键字内容替换
前言 工作中,我们可能遇到字符串内容替换的场景.例如:我们需要将一个字符串凡是 “#” 标注的,分别替换为不同的内容,那我们应该怎么做呢? 分析,一个字符串可能含有多个“#”,每个 “#”又对应不同的 ...
- C# DataGridView搜索
public static bool SearchDGV(DataGridView DGV, string strTxt, bool UpSearch = true, bool Show = fals ...
- Java基础-多线程-①线程的创建和启动
简单阐释进程和线程 对于进程最直观的感受应该就是“windows任务管理器”中的进程管理: (计算机原理课上的记忆已经快要模糊了,简单理解一下):一个进程就是一个“执行中的程序”,是程序在计算机上的一 ...
- SpringBoot在自定义类中调用service层等Spring其他层
解决方案: 1.上代码 @Component public class ServerHandler extends IoHandlerAdapter { @Autowired protected He ...
- java合并单元格同时导出excel
POI进行跨行需要用到对象HSSFSheet对象,现在就当我们程序已经定义了一个HSSFSheet对象sheet. 跨第1行第1个到第2个单元格的操作为 sheet.addMergedRegion(n ...