我们在前一章介绍了block的用法,而正确使用block必须要求正确理解block的内存管理问题。这一章,我们只陈述结果而不追寻原因,我们将在下一章深入其原因。

一、block放在哪里

我们针对不同情况来讨论block的存放位置:

1.栈和堆

以下情况中的block位于堆中:

  1. void foo()
  2. {
  3. __block int i = 1024;
  4. int j = 1;
  5. void (^blk)(void);
  6. void (^blkInHeap)(void);
  7. blk = ^{ printf("%d, %d\n", i, j);};//blk在栈里
  8. blkInHeap = Block_copy(blk);//blkInHeap在堆里
  9. }
  10. - (void)fooBar
  11. {
  12. _oi = 1;
  13. OBJ1* oj = self;
  14. void (^oblk)(void) = ^{ printf("%d\n", oj.oi);};
  15. void (^oblkInHeap)(void) = [oblk copy];//oblkInHeap在堆中
  16. }

2.全局区

以下情况中的block位于全局区:

  1. static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;};
  2. - (void)fooBar
  3. {
  4. int(^maxIntBlockCopied)(int, int) =[maxIntBlock copy];
  5. }
  6. void foo()
  7. {
  8. int(^maxIntBlockCopied)(int, int) = Block_copy(maxIntBlock);
  9. }

需要注意的是,这里复制过后的block依旧位于全局区,实际上,复制操作是直接返回了原block对象。

二、block引用的变量在哪里

 1.全局区

全局区的变量存储位置与block无关:

  1. static int gVar = 0;
  2. //__block static int gMVar = 1;
  3. void foo()
  4. {
  5. static int stackVar = 0;
  6. //    __block static int stackMVar = 0;
  7. }

注意:static变量是不允许添加__block标记的

2.堆栈

此时,你可能会问,当函数foo返回后,栈上的j已经回收,那么blkInHeap怎么能继续使用它?这是因为没有__block标记的变量,会被当做实参传入block的底层实现函数中,当block中的代码被执行时,j已经不是原来的j了,所谓物是人非就是这样吧~

另外,如果使用到变量j的所有block都没有被复制至heap,那么这个变量j也不会被复制至heap。

因此,即使将j++这一句放到blk()这句之前,这段代码执行后,控制台打印结果也是:1024, 1。而不是1024, 2

三、其他特性

1.复制的行为

对block调用复制,有以下几种情况:

1.对全局区的block调用copy,会返回原指针,并且这期间不处理任何东西(至少目前的内部实现是这样);

2.对栈上的block调用copy,每次会返回新复制到堆上的block的指针,同时,所有__block变量都会被复制至堆一份(多次拷贝,只会生成一份)。

3.对已经位于heap上的block,再次调用copy,只会增加block的引用计数。

为什么我们不讨论retian的行为?原因是并没有Block_retain()这样的函数,而且objc里面的retain消息发送给block对象后,其内部实现是什么都不做。

2.objc类中的block复制

objc类实例方法中的block如果被复制至heap,那么当前实例会被增加引用计数,当这个block被释放时,此实例会被减少引用计数。

但如果这个block没有使用当前实例的任何成员,那么当前实例不会被增加引用计数。这也是很自然的道理,我既然没有用到这个instance的任何东西,那么我干嘛要retian它?

我们要注意的一点是,我看到网上有很多人说block引起了实例与block之间的循环引用(retain-cycle),并且给出解决方案:不直接使用self而先将self赋值给一个临时变量,然后再使用这个临时变量。

但是,大家注意,我们一定要为这个临时变量增加__block标记(多谢第三篇文章回帖网友的提醒)。

这一章我们以结果导向的方式来说明了各种情况下,block的内存问题,下一章,我将剖析运行时库的源码,从根源阐述block的行为。也就是过程导向的方式了。

iOS中Block介绍(二)内存管理与其他特性的更多相关文章

  1. iOS之Block总结以及内存管理

    block定义 struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(v ...

  2. iOS中Block介绍(一)基础

    ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中 ...

  3. iOS中Block介绍 基础

    ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中 ...

  4. 看朋友日志发现的一个ios下block相关的内存管理问题,非常奇怪,请大家帮忙一起来回答!

    http://blog.csdn.net/fengsh998/article/details/38090205 这篇文章以下是我的回复.相同的代码仅仅是把变量的定义从局部变量改为类的成员变量就发现了非 ...

  5. iOS开发系列—Objective-C之内存管理

    概述 我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上).如果一个对象创建并使用后没 ...

  6. iOS经典面试题总结--内存管理

    iOS经典面试题总结--内存管理 内存管理 1.什么是ARC? ARC是automatic reference counting自动引用计数,在程序编译时自动加入retain/release.在对象被 ...

  7. iOS 中Block以及Blocks的使用,闭包方法调用

    OC: -(void)dataWithUrl:(NSString*)string AndId:(NSInteger)id returnName:(void(^)(NSString*name))back ...

  8. iOS学习笔记之ARC内存管理

    iOS学习笔记之ARC内存管理 写在前面 ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式. 指针变量与对象所有权 指针变量暗含了对其 ...

  9. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

随机推荐

  1. codeforces 652D . Nested Segments 线段树

    题目链接 我们将线段按照右端点从小到大排序, 如果相同, 那么按照左端点从大到小排序. 然后对每一个l, 查询之前有多少个l比他大, 答案就是多少.因为之前的r都是比自己的r小的, 如果l还比自己大的 ...

  2. Matlab插值计算各时刻磁法勘探日变观测值

    Matlab插值计算各时刻磁法勘探日变观测值 在磁法勘探中,消日变影响的改正称为日变改正.进行日变改正时必须设立日变站,观测日变情况.根据日变数据和测点观测时间,对观测数据进行改正. 在本次磁法实习中 ...

  3. windows常用环境变量

    %ALLUSERSPROFILE%列出所有用户Profile文件位置. %APPDATA%列出应用程序数据的默认存放位置. %CD%列出当前目录. %CLIENTNAME%列出联接到终端服务会话时客户 ...

  4. J2SE知识点摘记(十二)

    1.      File类 下面的构造方法可以用来生成File对象 File(String directoryPath) geName()用于返回文件名,getParent()返回父目录名,exist ...

  5. SVN利用Hooks自动发布网站

    出于管理需要,也需要在公司内部实现自动发布流程.当开发机提交SVN代码之后,自动同步到测试服务器进行测试. 网络的拓扑简化为: 1台核心SVN服务器,使用的svn管理软件为Subversion Edg ...

  6. 自写的LastPos,寻找字符串里的最后一个字符,RTL里没有提供这个函数——Delphi的String下标是从1开始的

    已经好几次了,没有这个函数还是感觉很不方便,所以自己写了一个: function LastPos(strFind :string; ch: Char): integer; var i, n: inte ...

  7. Redis用户添加、分页、登录、注册、加关注案例

    连接redis代码redis.php <?php //实例化 $redis = new Redis(); //连接服务器 $redis->connect("localhost&q ...

  8. android服务Service(上)- IntentService

    Android学习笔记(五一):服务Service(上)- IntentService 对于需要长期运行,例如播放音乐.长期和服务器的连接,即使已不是屏幕当前的activity仍需要运行的情况,采用服 ...

  9. openstack 的 policy 问题。

    想写nova的policy的实现, 但是发现网上,有人写的很不错了. ref: http://blog.csdn.net/hackerain/article/details/8241691 但是,po ...

  10. 轻奢请向历史SAY NO_重青网_重庆青年报_重庆青年报电子版_重庆青年报网站_重庆青年报官方网站

    轻奢请向历史SAY NO_重青网_重庆青年报_重庆青年报电子版_重庆青年报网站_重庆青年报官方网站 轻奢请向历史SAY NO 经济学家George Taylor在他著名的"裙摆指数" ...