[objective-c] ARC 补充

本文深入探讨了ARC(自动引用计数)在Objective-C中的工作原理及其带来的效率提升。文中不仅解释了ARC如何避免常见的内存管理误区,还提供了大量的示例代码帮助理解ARC的关键特性和注意事项。

可能的误区和一些小技巧

ARC听起来像自动内存管理,效率是不是比手动低呢?
ARC跟垃圾回收那种机制不同,根据Apple的说法,ARC的效率反倒高,如果你看过ARC的原理就知道了怎么回事了,具体参考文档吧。至于效率为什么会高,主要是之前通过autorelease释放的东西都是随着runloop在autorelease pool中一起释放的,而开启了ARC后,很多之前要autorelease的东西直接就通过类似手动管理的方式释放掉了,根本没放到autorelease pool中,从而提升了效率。那么效率提高了多少,反正我是感觉不出来,一般情况下应该微乎其微吧,但至不会降低效率。

我费了不少时间弄明白了内存管理的机制,用了ARC之前的努力岂不白费了?

  • 最开始我也比较排斥ARC,认为开启ARC,写代码不就没有什么技术含量了么?实际上使用ARC更需要我们对内存管理机制有深刻的认识,在没有ARC之前,即使我们不懂内存管理,我们程序照样跑,大不了我不写release,内存泄漏就泄漏呗!开启了ARC之后如果不懂内存管理机制,你连哪里出现问题了可能都不知道。
  • 此外ARC只是对NSxxx,UIxxx这些Objective-C对象有效,那些CGxxxx,CFxxxx这种Core Fundation对象的还是需要xxxxretain(),xxxxrelease()的,不懂内存管理机制很容易在Obj-C和Core Fundation转换的时候出现问题,记得有个老外的文章分析使用ARC之后,对象的生命周期出户意料的短,他举例的代码大概是这样的
    1CGColorRef cgcolor =  [UIColor blackColor].CGColor;
    2UIView *view = [UIView alloc] initWithFrame:frame];
    3[view.layer setBackgroundColor:cgcolor];
  • 在最后那行就会出现EXC_BAD_ACCESS,原因是第二行取得color的CGColor之后,color对象就被施放了。然后又汇编又怎么的分析了一通得出结论:color的生命周期短,之后还给出了几个fix我就不贴了,我只想说这根本不是生命周期短不短的问题,这就是他没好好看看Transitioning to ARC Release Notes这篇文档,像他这么个写法根本就是错的,不遵照内存管理肯定最后会有EXC_BAD_ACCESS。
  • ARC同样也不能处理retain cycle问题,综上也就是说想借ARC来免去学习内存管理是完全不可行的。

我的应用要向下兼容,所以暂时用不了ARC。

  • ARC是兼容iOS4.0+的,现在4.0以下的机器还有么,5.0以下的都挺少了吧,有也不用管了吧,苹果不能降级的好处就在这里,至于在5.0以下系统中不能使用weak,这个至少在我接触的各种情形中unsafe_unretained完全可以替代。

那些开源的Framework没有提供ARC版怎么办?

  • 可以针对单独的文件设置是否使用ARC,不使用的话就加上 -fno-objc-arc 标记,具体参考文档Transitioning to ARC Release Notes最后面。

有没有将现有代码转换成ARC的方法?

  • 菜单上 Edit>>Refactor>>Convert to Obj-c ARC… 之后按提示走就可以了,有时候可能很麻烦,一些开源Framework也几乎转不成功,这时需要按4中的方法,为那些不方便转换的文件提前设置好 -fno-objc-arc 标记,之后再Convert。

综合这一阵的体会,除了兼容4.0之前的系统外,基本上找不到任何一个不使用ARC的理由,实际情况中,开启ARC也确实方便了不少,不用费半天劲去再dealloc里面添加各种release,block的使用也更随意了,有ARC基本也不用担心内存泄漏问题,开发效率有了很大提高。

一,注意事项

1.不可以使用retain,retainCount,release,autorelease 用@select()这样的调用也不行.属性命名不能以new开头。

2.若重写一个类子类的dealloc,不应调用[super dealloc],当然也不用写什么release释放一些什么对象,只是处理一些你觉得必要处理的事情吧,比如中止一个还没有完成的网络请求.

3.不能使用NSAllocateObject和NSDeallocateObject

4.你不能在c结构中使用对象,更好的方式是使用Objective-c类来代替.

5.在id和void*之间不能隐私转换,必须指明相应转换的生命周期。

6.不能使用NSAutoreleasePool对象,ARC使用@autoreleasepool{}块代替。

7.不能使用内存块,NSZone已经不需要使用了,现在运行时已经忽略之。

二.基本关键字

声明变量就会默认赋值nil的.

1#相当于retain,在不再使用的时候被释放 (默认)
2strong
3 
4#与assign很像,不同在于如果指向的数据被释放了,那么这个指向nil
5weak
6 
7#相当于assign,指向的数据如果被释放,这个指向原来的地址
8unsafe_unretained

这些可以定义局部变量__strong __weak __unsage_unretained

1#标明传给函数的(id*)型参数是自动释放的,(函数中(id*)型参数默认的也是这种类型)
2__autoreleasing

1.__autoreleasing不能修饰全局变量,不能修饰类的属性.只能再函数块中用来定义变量,相当与__strong,一旦赋值,直到生命周期结束释放.
2.对于(id*)型函数参数,默认是__autoreleasing型的,这是由1知道只能传局部变量给函数做实参;如果用__weak修饰,那么会在结束时使的实参为nil,没有意义;__strong修饰可以用全局变量,类的属性值作为参数,其他与__autoreleasing没有区别

demo1

1NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", @"aaa"];
2NSLog(@"string: %@", string);
3#打印:(null)
4#编译器会给出警告.程序第一句申请了一个内存,赋值给string,然后weak引用的string并没有retain这个内存
5#这个内存赋值后在块内并没有再次使用,他的周期就存在与赋值语句内部,所以释放,然后string指向的内存被释放所以他是nil.

demo2

01@property(nonatomic,strong) NSString* stringA;
02@property(nonatomic,weak) NSString* stringB;
03- (void)a {
04    self.stringA = @"string A";
05    self.stringB = stringA;
06    self.stringA = nil;
07    NSLog(@"%@",stringB);
08}
09-(void)b{
10    self.stringA = [NSString stringWithFormat:@"%@",@"string A"];
11    self.stringB = stringA;
12    self.stringA = nil;
13    NSLog(@"%@",stringB);
14}
15 
16- (void)c {
17    self.stringA = [[NSString alloc] initWithFormat:@"%@",@"string A"];
18    self.stringB = stringA;
19    self.stringA = nil;
20    NSLog(@"%@",stringB);
21}

��
这三个函数分别输出什么呢?都是”string A”.那么在函数执行完之后呢?stringB对应的值是什么?

a:string A// 常字符串存储在静态存储区,stringB指向的那个不会被释放
b:(null)// stringA指向那块自动释放的内存再块结束时释放
c:(null)// stringA就是指向分配字符串的内存,赋予nil会释放那块内存
btw:第一次试验的时候块内就已经是块外的值了.囧..

三.Toll-Free Bridging

Core Foundation 对象与Objective-c对象之间的赋值,函数调用参数相互转化时需要用到的关键字

1#简单赋值,不会影响两边对象的retain count.
2__bridge
3 
4#赋值后释放右边的对象
5__bridge_transfer
6 
7#赋值后也保留不释放右边的对象
8__bridge_retained

demo3

01-(void)test
02{
03    CFStringRef coreFoundationString = CFStringCreateWithCString(CFAllocatorGetDefault(),"C String", kCFStringEncodingUTF8);    // 创建 retainCount = 1
04    id unknownObjectType = (__bridge id)coreFoundationString; // 简单赋值,不变,retainCount = 1
05    CFStringRef anotherString = (__bridge_retained CFStringRef)unknownObjectType; // 保留赋值,加一,retainCount = 2
06    NSString *objCString = (__bridge_transfer NSString *)coreFoundationString; // 释放赋值,减一,retainCount =1;由于NSString*默认strong,加一,retainCount = 2
07    NSLog(@"String = %@", objCString);
08    objCString = nil;   // 不再指向原内存,原内存减一,retainCount = 1
09    CFRelease(anotherString);   // 释放,减一,retainCount = 0
10}

四.其他

1.开关 :-fobjc-arc 和 -fno-objc-arc 编译标识来指明单个文件的方式.
整个工程转向ARC可以选中工程->菜单edit->refactor->convert to Objective-c ARC

2.除了nib的Top-Level(main nib文件里面的对象,除了File’s owner 和 Application)对象,其他的IBOutlet最好都是weak型的.

3.如果一定要实现类的自定义的retain和release,那么同时也要在类中加入

1-(BOOL)supportsWeakPointers { return NO; }

4.在c型的结构中使用objective-c对象
使用void*代替id;或者使用__unsage_unretained 修饰objective-c对象

5.不能zeroing-weak引用的类有

1SATSTypesetter, NSColorSpace, NSFont, NSFontManager, NSFontPanel, NSImage, NSMenuView, NSParagraphStyle, NSSimpleHorizontalTypesetter, NSTableCellView, NSTextView, NSViewController, NSWindow, and NSWindowController.

他们的对象,作为属性,使用assign代替weak;作为变量,使用__unsafe_unretained代替__weak。
ARC下也不能weak引用NSHashTable, NSMapTable, or NSPointerArray。

6. __block修饰的变量默认也是__strong引用的.

转载自 链接 链接

官网文档 Transitioning to ARC Release Note 中文版

Copyright © 2010 c小淡のп_п_п 转载请著名出处

推荐给您的文章:
  1. [objective-c] ARC 对象转换
  2. [objective-c] 分类(category)和扩展(extension)
  3. [objective-c] wwdc2012 objc新特性
  4. [objective-c] 消息, category和protocol
  5. [tutorial] programming with openGLES chapter 01

arc , autorelease , objective-c , release , retain , strong , week , __bridge

From: http://www.stylejar.com/archives/objective-c-arc-%E8%A1%A5%E5%85%85.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值