[读书笔记]iOS与OS X多线程和内存管理 [Blocks部分-4]

2.3.5 __block变量存储域

Block变量从栈复制到堆时对__block变量产生的影响


__block变量存储域

影响


从栈复制到堆并被Block持有


被Block持有

在多个Block中使用__block变量时,第一个Block从栈复制到堆时,__block变量也一并从栈复制到堆。在之后的Block从栈复制到堆时,__block变量被Block持有,__block变量引用计数增加。之前__block变量结构中的__forwarding能够做到“不管__block变量在栈上还是堆上都能访问”,原因是栈上的__block变量在复制到堆上时,他的成员变量__forwarding的值替换为复制后堆上的__block变量的地址。

2.3.6 截获对象

看下面代码:


   
typedef 
void (^aBlock)(
id);

aBlock
ablock;

{

id
array=[[NSMutableArrayalloc]init];

ablock=^(id
objct){

[array addObject:objct];

NSLog(@"array count %d",[arraycount]);

};

}

ablock([[NSObjectalloc]init]);

ablock([[NSObjectalloc]init]);

作者写到:“执行该代码后,程序会强制结束,array随变量作用域的结束而被废弃”。但在我的实际运行中,array对象正常使用,程序也正常运行(Xcode5.1.1和Xcode6.1)。所以可能苹果已经修改这方面的机制,作者关于截获对象的结论不再列出,使用时截获对象的规则可参照截获自动变量的规则。

这一节提到_Block_object_assign函数和_Block_objec_dispose函数,前者相当于retain方法,将对象赋值在对象类型的结构体成员变量中(例如上面的代码中将一个对象加入到array中);后者相当于release方法,释放赋值在对象类型的结构体成员变量中的对象(如从array中释放某个object)。

以下情况Block会从栈复制到堆:

  • 调用Block的copy实例方法
  • Block作为函数返回值
  • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  • 在方法名含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时。

除以下情景外,推荐调用Block的copy实例方法(即如果系统已自动调用,我么就不需调用):

  • Block作为函数值返回时
  • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  • 在方法名含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时。

2.3.7 __block 对象和变量

此节沿用上节结论,略过

2.3.8 Block循环引用


Person.m文件代码

主程序代码:

{

...

Person *per=[[Personalloc]init];

NSLog(@"o");

...

}

执行结果是该源码中的dealloc方法未被调用.原因是,Person类的对象持有blk_,blk_中持有id类型变量self.并且由于Block语法赋值给成员变量blk_(此时为ARC模式,变量默认为__strong类型),所以栈上的Block复制到堆,并持有所使用的self,这就造成了循环引用。编译器也给出了警告。

为避免循环引用可使用__weak修饰变量,并将self赋值给变量使用。如下:


-(id)init{

self=[superinit];

id
__weak tmp=self;

blk_=^{

NSLog(@"self %@",tmp);

};

return
self;

}

在该代码中,由于当Block存在时,持有此Block的Person对象肯定存在(blk_属于Person对象),所以不需要对tmp进行判空。

在iOS4及更低版本中,没有ARC机制,使用__unsafe_unretained修饰符代替__weak修饰符。

下面的代码也会造成循环引用,因为Block语法内使用的obj属于对象的成员变量,想要截获对象的成员变量,自然也截获了对象本身self.


//arc打开状态

@interface
Person(){

void(^blk_)(void);

id
obj;

}

@end

@implementation
Person

-(void)dealloc{

NSLog(@"person dealloc");

}

-(id)init{

self=[superinit];

blk_=^{

NSLog(@"obj %@",obj);

};

return
self;

}

使用__block变量也能避免循环引用,如下代码:


//arc打开状态

@interface
Person(){

void(^blk_)(void);

}

@end

@implementation
Person

-(void)dealloc{

NSLog(@"person dealloc");

}

-(id)init{

self=[superinit];

__block
id tmp=self;

blk_=^{

NSLog(@"obj %@",tmp);

tmp=nil;

} ;

return
self;

}

-(void)execBlock{

blk_();

}


该代码没有引起循环引用,但是如果不调用execBlock这个方法,即不执行对tmp的赋值,就会引起循环引用。

  • Person类对象持有Block;
  • Block持有__block变量
  • __block持有Person类对象

通过执行execBlock,__block变量tmp对Person类对象的强引用失效。

使用__block变量避免循环引用有如下优点,

  • 通过__block变量可控制对象的持有时间
  • 在不能使用__weak修饰符的环境下代替__unsafe_unretained修饰符,不必担心悬垂指针。

缺点是为避免循环引用必须执行Block。

2.3.9 copy/release

ARC无效时,需要手动将Block从栈复制到堆,用copy方法来复制,release方法来释放。只要Block位于堆上,则可以通过retain方法持有,对于栈上的Block调用retain方法不起任何作用。在c语言中也可以使Block,此时使用Block_copy和Block_release代替copy/release实例方法。另外,ARC无效时,__block说明符被用来避免Block循环引用,这是因为Block从栈复制到堆时,若Block使用的变量为附有__block说明符的id类型对象或对象类型自动变量,不会被retain,反之如果没有__block说明符则会被retain。

在ARC有效和无效时,__block说明符的用途大不一样,请注意。

Block部分到此结束。

时间: 2024-10-26 07:49:40

[读书笔记]iOS与OS X多线程和内存管理 [Blocks部分-4]的相关文章

[读书笔记]iOS与OS X多线程和内存管理 [Blocks部分-2]

2.3 Blocks的实现 2.3.1 Block的实质 通过命令"clang -rewrite-objc 文件名"能够将含有Block语法的源代码转换为C++源代码. 含有Block的源代码如下: #include <stdio.h>//不导入库文件无法运行 int main() { void(^testBlock)(void)=^{ printf("i am testBlock"); }; testBlock(); } 转换后的代码有五百行左右,这里

[读书笔记]iOS与OS X多线程和内存管理 [Blocks部分-3]

2.3.2 截获自动变量 通过转换后的源码可以发现,Block语法中使用的自动变量被作为成员变量追加到__main_block_impl_0结构体中,Block中没有使用的自动变量不会被追加,所以Block的变量截获只针对Block使用的自动变量. 源码: #include <stdio.h>//不导入库文件无法运行 int main() { int val1=0; int val2=10; void(^testBlock)(void)=^{ printf("i am testBlo

[读书笔记]iOS与OS X多线程和内存管理 [Blocks部分-1]

第二章 Blocks 2.1 Blocks摘要 Blocks是C语言的扩充功能,是带有自动变量(局部变量)的匿名函数. Blocks保存自动变量的值. Blocks不是Objective-c独有的概念,在其他语言中(如Smalltalk或Ruby)也有对应的概念. 2.2 Blocks模式 2.2.1 Block语法 ^ 返回值类型 参数列表 表达式 返回值类型可以省略 ^ 参数列表 表达式 参数列表也可以省略 ^ 表达式 2.2.2 Block类型变量 可将Block语法赋值给声明为Block

[读书笔记]iOS与OS X多线程和内存管理 [GCD部分]

3.2 GCD的API 苹果对GCD的说明:开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中. "Dispatch Queue"是执行处理的等待队列.通过dispatch_async函数等API,在Block语法中记述想执行的处理并追加到Dispatch Queue中,Dispatch Queue按照追加的顺序,执行处理. Dispatch Queue分为两种: 种类 说明 Serial Dispatch Queue 等待现在执行中处理结束(顺序执行) Co

iOS核心语言Objective C语言 —— 内存管理

本分享是面向有意向从事iOS开发的伙伴以及苹果产品的发烧友们,或者已经从事了iOS的开发者,想进一步提升者.如果您对iOS开发有极高的兴趣,可以与我一起探讨iOS开发,一起学习,共同进步.如果您是零基础,建议您先翻阅我之前分享的iOS开发分分钟搞定C语言系列,然后在开始Objective C语言的学习,如果您遇到问题也可以与我探讨,另外将无偿分享自己整理出来的大概400G iOS学习视频及学习资料,都是干货哦!可以新浪微博私信?关注极客James,期待与您的共同学习和探讨!!由于时间有限,每天在

iOS开发——修改指定文件的内存管理状态

今天项目要上线,在Archive时报错:  ARC forbids explicit message send of 'release' 'release' is unavailable: not available in automatic reference counting mode   项目中有几个第三方库用到MRC,在release时报错.在运行时没有用到这个库所以没有报错(没有确定).于是我改了那几个文件为MRC状态,成功Archive,在ARC改为-fobjc-arc,MRC为-f

读书笔记——IOS之Web开发

Web开发 读书笔记:http://www.mincoder.com/article/2576.shtml 主要的请求方法 Get请求:数据以明文在URL中,数据量小 Post请求:数据大小没有限制 HEAD请求:请求头信息 程序的实现需要借助几个对象: NSURLRequest:建立了一个请求,可以指定缓存策略.超时时间.和NSURLRequest对应的还有一个NSMutableURLRequest,如果请求定义为NSMutableURLRequest则可以指定请求方法(GET或POST)等信

Objective-C高级编程:iOS多线程及内存管理(第一章翻译)

写在翻译之前:当初看到这本书的时候,感觉深入浅出讲得比较到位,但是在市面上看到的翻译版本翻译的却没有原著的精髓和味道.所以产生了自己将其翻译一下给初学者一些便利的想法.所以才有了这个系列的第一章的翻译.目前剩余的部分依然在翻译过程中,估计不久之后就可以陆续地发出了. 因为本人的水平或者用词问题,本翻译难免有不周详或不正确之处.如果有人看到还望指出,我一定会尽力地修改那些不正确的部分,让更多的人可以看到更优质的资料. Chapter 1 Life before Automatic Referenc

【iOS开发-33】学习手动内存管理临时抛弃ARC以及retain/assign知识——iOSproject师面试必考内容

我们为什么须要内存管理?当使用内存达到40M和45M时候会发出警告,假设不处理,占用内存达到120M时直接强制关闭程序. 所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对象的过程:先分配内存空间存储对象:初始化成员变量:返回对象的指针. (2)对象在创建时,内部会自己主动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一推断标记.release会-1.retain会+1,retain后返回的是自