Xcode ARC的引入

本文深入讲解了Objective-C的自动引用计数(ARC)机制,包括如何在项目中启用ARC、解决常见编译错误及自由桥接转换等。此外,还探讨了ARC模式下对第三方库的支持问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很多引用的类库暂时还不支持.麻烦的是对大多数第三方库需要加禁用arc的编译flag

如果你的项目使用的是 ARC 模式,则为非 ARC 模式的代码文件加入 -fno-objc-arc 标签。

添加标签的方法:

  1. 打开:你的target -> Build Phases -> Compile Sources.
  2. 双击对应的 *.m 文件
  3. 在弹出窗口中输入上面提到的标签 -fobjc-arc / -fno-objc-arc
  4. 点击 done 保存


虽然objective-c 2.0支持垃圾收集器(Garbage Collection,简称GC),但是垃圾收集器只能在MAC程序上开启,ios上没办法用。就算在MAC应用程序上可以用,GC也会有性能上的损耗。 
现在LLVM3.0多了一个给力的新东西,叫Automatic Reference Counting。


ARC是Objective-C编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所做的只不过是在代码编译时为你自动在合适的位置插入release或autorelease,就如同之前MRC时你所做的那样。


使用Xcode自带的转换ARC工具
165  
166 选择要转换的文件
167  
168 这个小工具是Edit->Refactor下的Convert to Objective-C ARC


SURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
184 if (fileURL != nil) {
185   SystemSoundID theSoundID;
186   OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)fileURL, &theSoundID);
187   if (error == kAudioServicesNoError) {
188       soundID = theSoundID;
189     }
190 }
191  
192 这里代码尝试把一个NSURL指针强制转换为一个CFURLRef指针。这里涉及到一些Core Services特别是Core Foundation(CF)的东西,AudioServicesCreateSystemSoundID()函数接受CFURLRef为参数,这是一个CF的概念,但是我们在较高的抽象层级上所建立的是NSURL对象。在Cocoa框架中,有很多顶层对象对底层的抽象,而在使用中我们往往可以不加区别地对这两种对象进行同样的对待,这类对象即为可以”自由桥接”的对象(toll-free bridged)。NSURL和CFURLRef就是一对好基友好例子,在这里其实CFURLRef和NSURL是可以进行替换的。
193  
194 通常来说为了代码在底层级上的正确,在iOS开发中对基于C的API的调用所传入的参数一般都是CF对象,而Objective-C的API调用都是传入NSObject对象。因此在采用自由桥接来调用C API的时候就需要进行转换。但是在使用ARC编译的时候,因为内存管理的原因,编译器需要知道对这些桥接对象要实行什么样的操作。如果一个NSURL对象替代了CFURLRef,那么在作用区域外,应该由谁来决定内存释放和对象销毁呢?为了解决这个问题,引入了bridge,bridge_transfer和__bridge_retained三个关键字。关于选取哪个关键字做转换,需要由实际的代码行为来决定。如果对于自由桥接机制感兴趣,大家可以自己找找的相关内容,比如适用类型、内部机制和一个简介~之后我也会对这个问题做进一步说明
195  
196 回到demo,我们现在在上面的代码中(CFURLRef)前加上__bridge进行转换。


为了方便查找,再此列出一些在转换时可能出现的问题,当然在我们使用ARC时也需要注意避免代码中出现这些问题:
211  
212 “Cast … requires a bridged cast”
213 这是我们在demo中遇到的问题,不再赘述
214  
215 Receiver type ‘X’ for instance message is a forward declaration
216 这往往是引用的问题。ARC要求完整的前向引用,也就是说在MRC时代可能只需要在.h中申明@class就可以,但是在ARC中如果调用某个子类中未覆盖的父类中的方法的话,必须对父类.h引用,否则无法编译。
217  
218 Switch case is in protected scope
219 现在switch语句必须加上{}了,ARC需要知道局部变量的作用域,加上{}后switch语法更加严格,否则遇到没有break的分支的话内存管理会出现问题。
220  
221 A name is referenced outside the NSAutoreleasePool scope that it was declared in…
222 这是由于写了自己的autoreleasepool,而在转换时在原来的pool中申明的变量在新的@autoreleasepool中作用域将被局限。解决方法是把变量申明拿到pool的申请之前。
223  
224 ARC forbids Objective-C objects in structs or unions
225 可以说ARC所引入的最严格的限制是不能在C结构体中放OC对象了..因此类似下面这样的代码是不可用的
226  
227 1
228 2
229 3
230 4
231  
232 typedef struct {
233   UIImage *selectedImage;
234   UIImage *disabledImage;
235 } ButtonImages;
236  
237 这个问题只有乖乖想办法了..改变原来的结构什么的..

另外,在MRC时代一个常做的事情是在dealloc里把指向自己的delegate设成nil(否则就等着EXC_BAD_ACCESS吧 :P),而现在一般delegate都是weak的,因此在self被销毁后这个指针自动被置成nil了,你不用再为之担心,好棒啊..

通过加载xib得到的用户界面,在其从xib文件加载时,就已经是view hierarchy的一部分了,而view hierarchy中的指向都是strong的。因此outlet所指向的UI对象不应当再被hold一次了。将这些outlet写为weak的最显而易见的好处是你就不用再viewDidUnload方法中再将这些outlet设为nil了(否则就算view被摧毁了,但是由于这些UI对象还在被outlet指针指向而无法释放,代码简洁了很多啊..)。


自由桥接的细节
349  
350 MainViewController现在剩下的问题都是桥接转换问题了~有关桥接的部分有三处:
351  
352 (NSString *)CFURLCreateStringByAddingPercentEscapes(…):CFStringRef至NSString *
353 (CFStringRef)text:NSString *至CFStringRef
354 (CFStringRef)@“!‘();:@&=+$,/?%#[]”:NSString 至CFStringRef
355 编译器对前两个进行了报错,最后一个是常量转换不涉及内存管理。
356  
357 关于toll-free bridged,如果不进行细究,NSString和CFStringRef是一样的东西,新建一个CFStringRef可以这么做:
358  
359 1
360  
361 CFStringRef s1 = [[NSString alloc] initWithFormat:@"Hello, %@!",name];
362  
363 然后,这里alloc了而s1是一个CF指针,要释放的话,需要这样:
364  
365 1
366  
367 CFRelease(s1);
368  
369 相似地可以用CFStringRef来转成一个NSString对象(MRC):
370  
371 1
372 2
373 3
374 4
375 5
376  
377 CFStringRef s2 = CFStringCreateWithCString(kCFAllocatorDefault,bytes, kCFStringEncodingMacRoman);
378 NSString *s3 = (NSString *)s2;
379  
380 // release the object when you're done  
381 [s3 release];
382  
383 在ARC中,编译器需要知道这些指针应该由谁来负责释放,如果把一个NSObject看做是CF对象的话,那么ARC就不再负责它的释放工作(记住ARC是only for NSObject的)。对于不需要改变持有者的对象,直接用简单的bridge就可以了,比如之前在SoundEffect.m做的转换。在这里对于(CFStringRef)text这个转换,ARC已经负责了text这个NSObject的内存管理,因此这里我们需要一个简单的bridge。而对于CFURLCreateStringByAddingPercentEscapes方法,方法中的create暗示了这个方法将形成一个新的对象,如果我们不需要NSString转换,那么为了避免内存的问题,我们需要使用CFRelease来释放它。而这里我们需要一个NSString,因此我们需要告诉编译器接手它的内存管理工作。这里我们使用bridge_transfer关键字,将内存管理权由CF object移交给NSObject(或者说ARC)。如果这里我们只用bridge的话,内存管理的负责人没有改变,那么这里就会出现一个内存泄露。另外有时候会看到CFBridgingRelease(),这其实就是transfer cast的内联写法..是一样的东西。总之,需要记住的原则是,当在涉及CF层的东西时,如果函数名中有含有Create, Copy, 或者Retain之一,就表示返回的对象的retainCount+1了,对于这样的对象,最安全的做法是将其放在CFBridgingRelease()里,来平衡retain和release。
384  
385 还有一种bridge方式,__bridge_retained。顾名思义,这种转换将在转换时将retainCount加1。和CFBridgingRelease()相似,也有一个内联方法CFBridgingRetain()来负责和CFRelease()进行平衡。
386  
387 需要注意的是,并非所有的CF对象都是自由桥接的,比如Core Graphics中的所有对象都不是自由桥接的(如CGImage和UIImage,CGColor和UIColor)。另外也不是只有自由桥接对象才能用bridge来桥接,一个很好的特例是void (指向任意对象的指针,类似id),对于void 和任意对象的转换,一般使用_bridge。(这在将ARC运用在Cocos2D中很有用)

(http://www.oschina.net/code/snippet_196012_24966)

二、开启ARC后需要遵循的原则 
3、不能使用id<–>void *的类型转换,因为编译器不知道这个void *是否需要retained。 
4、不能使用NSAutoreleasedPool,要用@autoreleasepool {„„}来代替。 

在新旧技术更替的更年期应注意:现在ARC还under NDA,没有任何开源框架可以公开支持ARC的,如果想使用此特性,在选取开源框架时应该尽量选择使用CF库较少的框架,否则手动爆开源框架的菊花会很痛苦。

【iOS中的自由桥接
http://www.cnblogs.com/crazypebble/p/3381260.html】】
http://wenku.baidu.com/link?url=-eEFmXc76ar9IolULQjtPXCB3qYBwZRSb0vL0rfT7SmHXYH5hHBCGyVCQYIE-JWyhgvNRzdaLnv6woNdH5dYYMJbDzDudtoEmmXox_OKh7O
http://blog.sina.com.cn/s/blog_4c4c79950100t3uy.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值