ARC 基础(上)

ARC 基础(上)

 

    IOS5 中最具颠覆性的变化当属自动引用计数(Automatic Reference Counting)的引入,缩写为 ARC。ARC 是新的 LLVM 3.0 编译器具有的特性之一,这项技术彻底摒弃了让全部 IOS 开发者由爱生恨的手动内存管理。在你的工程中使用 ARC很是简单。你还像往常同样编程,只是再也不调用 retain,release 和 autorelease 了。这基本上就是 ARC 的所有。若是开启了自动引用计数,编译器就会在你程序中的恰当位置,插入 retain,release 和 autorelease。你没必要再为这个操心,由于编译器会替你搞定。html

 

    ARC 基本上是 LLVM 3.0 编译器而非 iOS5 的新特征,因此你也能够将它用在 iOS4.0 及以上版本上。ARC 惟一须要 iOS5 的部分是弱指针。这意味着若是你想在 iOS4 上 部署 ARC,你就不能使用弱属性或者__weak 变量。 git

    认识到 ARC 是 objective-c 编译器的一个特性是很重要的,所以与 ARC 相关的一切都发生在构建你的程序时。ARC 不是一个运行时特性(有一小部分例外,就是弱指针系统), 它也不是你从其余语言了解的垃圾回收。ARC 所做的只是在编译代码时向其中插入 retain 和 release,就在你会本身添加他们的地 方 -- 或者至少是你应该添加他们的地方。这就使 ARC 和手动管理代码同样快速,有时候 甚至更快一些,由于它私下能够执行某些优化。 github

    诸如firstName 和 textField 这类指针称为"强"(""strong")指针,由于他们保持了对象的存在。默认状况下,成员变量和局部变量是强指针。 一样也存在"弱"指针("weak" pointer)。弱指针变量仍然何以指向对象,可是再也不成为全部者。web

1 __weak NSString *weakName = self.textField.text;

    weakName 指针变量指向和 textField.text 属性相同的 string 对象,但再也不是全部者。若是文本框的内容改变,该 string 对象再也不有全部者,因此被释放了。当这发生(对象被释放)时,weakNae 的值被自动置为 nil。即所谓"归零"("zeroing")弱指针。 objective-c

    注意这是特别便利的特性,由于它防止了弱指针指向已被释放的内存。这类事情过去曾致使了大量的 BUG -- 你可能据说过"野指针" 或者 "僵尸" -- 可是感谢这些归零弱指针,那些事再也不会发生!你大概不会频繁使用弱指针。他们在两个对象是父子关系时最有用。父母会对孩子拥有 强指针 --- 所以"拥有"孩子 -- 可是为了防止全部权循环,孩子仅对父母拥有弱指针。 编程

1 __weak NSString *str = [[NSString alloc] initWithFormat:...]; 
2 NSLog(@"%@", str); // will output "(null)"

    string 对象没有全部者(由于 str 是弱指针),因此对象会在建立后马上被释放。Xcode 在你作这件事的时候会给出一个警告由于这可能并不是是你所但愿发生的事情("Warning: assigning retained object to weak variable; object will be released after assignment")。 xcode

    自动引用计数也有一些限制。做为起步,ARC 只对 objective-c 有效。若是你的程序使用 core fundation 或者 malloc()和 free(),那么你仍然对其内存管理负有责任。 工具

    AFHTTPRequestOperation.h/.m: AFNetworking 库的一部分,它使对 web service 的请 求更易于执行。 https://github.com/gowalla/AFNetworking post

    SVProgresHUD.h/.m/.bundle 一个会在搜索时显示于屏幕上进度指示器。你之前可能 没见过,bundle 文件。这是一个特殊类型的文件夹,它包含了 SVProgressHUD 要用 到的图片文件。优化

要查看这些文件,能够右键点击.bundle 文件,选择“查看包内容” (Show Package Contents)菜单选项。 https://github.com/samvermette/SVProgressHUD 

 1.xcode 有一个自动转化工具,可以转换你的代码。

 2.你能够手工转化这些文件。

 3.你能够对你不但愿转化的源文件禁用 ARC。 

    ARC 是新的 LLVM 3.0 编译器的一个特性。你的现有工程极可能使用的是早先的 gcc 4.2 或者 LLVM-GCC 编译器,因此你应该首先将工程切换到新编译器,看看在非 ARC 模式下是否存在错误。

转到 Project Settings 屏幕,选中 Artists target,在 Build Setting 的搜 索框中输入"compiler"。修改"Compiler for C/C++/Objective-C"选项,选择 Apple LLVM compiler 3.0+。

    在 warning 抬头部分,还要将"Other Warning Flags"设置为-wall。编译器如今会检查全部可能致使问题的情况。默认状况下,大多数这类警告都被关掉了,可是我发现老是将他们当 做致命错误是颇有用的。

   在 Build Options 抬头下的将 Run Static Analyzer 设置为YES.

    Build Settings 屏幕中,切换到"All"来查看全部可用的设置项(和 Basic 不同, Basic 仅仅显示经常使用设置项)。搜索"automatic"并设置"Objective-C Automatic Reference Counting"选项为 YES

    xcode 有一个自动转化工具,可以转换你的代码,选择"Edit\Refactor\Convert to Objective-C ARC"。 

    通常说来,iOS 中基于 C 的 API 会使用 core fundation 对象(CF 就表明它),而基于 Objective-C 的 API 使用由 NSObject 扩展而来的真正的对象。有时候你须要在这两者之间 进行转换,而这是免费桥接技术所容许的。 

    Apple 引入了一组关键字:__bridge, __bridge_transfer 和 __bridge_retained 。

    @property (strong, nonatomic)  strong 关键字表明你的意图。它告诉 ARC 属性背后被同步的 ivar 拥有对对象的强引用。也就是说,window 属性包含了一个指向 UIWindow 对象的指针,同时它也成为了 UIWindow 对象的全部者。只要 window 属性还保存着 UIWindow 对象的值,它(UIWindow 对象)就仍然存在于内存中。 

 

    在 ARC 之前,若是属性被声名为 retain,下面的代码会致使内存泄漏: 

1 self.someProperty = [[SomeClass alloc] init];

    init 方法返回一个被保留的对象,而将其赋给这个属性会再次保留它。这就是你为何 要用 autoreleae,为了平衡 init 方法中的 retain。可是用 ARC 的话,上面的代码是没问题的。

编译器足够聪明,它知道这里不该该作两次 retain。 

    我喜好 ARC 的特性之一是,在大多数状况下,你都没必要写 dealloc 方法。 有时dealloc 方法仍是必要的。大多数状况下,你能够忘掉 dealloc,编译器会帮你 搞定。可是有时,你仍是须要手动释放资源。这个类就是这种状况。

当 SoundEffect 对象被 析构时,咱们仍然须要调用 AudioServicesDisposeSystemSoundID()来清理声音对象,dealloc 就是调用它的绝佳位置。 

    类扩展很酷的地方在于,它容许你为类添加私有属性和方法名。若是你不但愿将某些属 性或方法暴露在公共的@interface 中的话,你能够用类扩展。 

    咱们将 Build Settings 内的 Objective-C Automatic Reference Counting 设置为 YES 时,已经在程序范围内启用了 ARC。可是经过使用-fno-objc-arc 标志,你可让编译器对特定的文件不使用 ARC。

xcode 会以关闭 ARC 的方式对这些文件进行编译。 

    "Cast ... requires a bridged cast"

    这是咱们以前碰到过的。若是编译器不能本身肯定如何转换,他会期待你插入一个 __bridge 修饰符。另外还有两种 bridge 类型,__bridge_trasfer 和__bridge_retained,你要使用 哪个彻底取决于你想作什么。 

    "ARC forbids Objective-C objects in structs or unions"

    ARC 的限制之一是,你再也不可以将 Objective-C 对象放到 C 结构中了。 

    通常状况下,实例变量应该是 类的内部实现的一部分,而不是你想暴露在公共接口中的东西。对类的使用者来讲,类的成 员变量是什么可有可无。

从数据隐藏的角度来看,咱们应该将这些实现细节放到类的 @implementation 部分。 

复制代码
 1 #import <UIKit/UIKit.h>
 2 @interface MainViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>
 3 @property (nonatomic, retain) IBOutlet UITableView *tableView;
 4 @property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
 5 @end 
 6 
 7 @implementation MainViewController {
 8   NSOperationQueue *queue;
 9   NSMutableString *currentStringValue;
10 } 
复制代码

这项技术让你的.h 文件更加简洁,而将成员变量放到他们真正属于的地方。 

保留 dealloc 方法的惟一状况是,当你须要释放某些不在 ARC 保护伞之下的资源时。好比说对 Core Fundation 对象调用 CFRelease(),对使用 malloc()分配的内存调用 free(),反注 册通知,中止记时器,等等。

做为最佳实践,若是你定义了一个属性,你应该老是使用属性。惟一须要访问属性背后的成员变量的地方是在 init 中,或者当你提供自定义的 getter 和 setter 方法时。这就是为何同步声明常常修改为员变量的缘由: 

@synthesize propertyName = _propertyName; 

这种结构将防止你意在使用"self.propertyName"的时候,输入"propertyName"从而误用了 其背后的成员变量。 

 

@property (nonatomic, weak) IBOutlet UITableView *tableView;

@property (nonatomic, weak) IBOutlet UISearchBar *searchBar; 

对于全部的 outlet 属性推荐使用弱关系。这些视图对象已是视图控制器层次结构的一部分了,不须要在别的地方 retain 它。

将 outlet 声明为弱引用的最大好处是,你不须要再花时间编写你的 viewDidUnload 方法了。 

由于 tableView 和 searchBar 属性是弱引用,他们在所指向的对象被销毁后自动被 设为 nil。这就是咱们所谓的“归零”弱指针。 

• strong. 是 retain 的同义词。一个强属性会成为所指向对象的全部者。

• weak. 这个属性表明一个弱指针。当所指向的对象被释放时,他会自动被设为 nil。记 住,对于 outlet 使用它。

• unsafe_unretained.这是原来的"assign"的同义词。它只在特殊状况下以及你想将目标设 为 iOS4 时使用。后面会讲到它。

• copy. 这仍是和之前同样。这将制做对象的一份拷贝,并建立强关系。
• assign. 你能再为对象使用它了,但你仍是能够用于基础类型如 BOOL, int 和 float。 

 

CFSTR()宏用一个指定的字符串建立一个 CFStringRef 对象。这个字符串常量是一个标 准的 C 字符串,因此不须要以@打头。不须要将 NSString 对象转换为 CFStringRef,咱们能直接获得一个 CFStringRef 对象。 

当你在两个世界之间移动对象时,桥接转换(bridged cast)是必要的。一边是 objective-c 的世界,另外一边是 Core Foundation。 

在全部状况下,NSString 和 CFStringRef 能够看成一个东西对待。你能够接受一个 NSString 对象,把它看成一个 CFStringRef 对象,或者将 CFStringRef 对象用做 NSString。这 就是免费桥接背后的思想。以前这只须要作一个简单的转换: 

复制代码
 1 CFStringRef s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name]; 
 2 
 3 // ....
 4 
 5 CFRelease(s1); 
 6 
 7 
 8 CFStringRef s2 = CFStringCreateWithCString(kCFAllocatorDefault, bytes, kCFStringEncodingMacRoman); 
9 NSString *s3 = (NSString *)s2; 10 // release the object when you're done 11 [s3 release];
复制代码

如今咱们有了 ARC,编译器须要知道谁负责释放那些转换的对象。若是你将 NSObject 做为 Core Fundation 对象,那么 ARC 不会负责释放它。但你确实须要告诉 ARC 你的意图, 编译器不能本身来推断。一样的,若是你建立了一个 Core Fundation 对象但将其转换为了 NSObject 对象,你就须要告诉 ARC 获得它的全部权,并及时释放它。这就是桥接转换要作 的。 

复制代码
 1 - (NSString *)escape:(NSString *)text
 2 {
 3   return (NSString *)CFURLCreateStringByAddingPercentEscapes(
 4     NULL,
 5     (__bridge CFStringRef)text,
 6     NULL,
 7     CFSTR("!*'();:@&=+$,/?%#[]"),
 8       CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
 9 } 
复制代码

• __bridge 当你想将一种类型临时看成另外一种类型使用,而不转移全部权时,须要使用__bridge, ARC 仍旧管理着它。

• __bridge_transfer: 给予 ARC 全部权

1 NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];
2 CFStringRef s2 = (__bridge_retained CFStringRef)s1;
3 // do something with s2 // . . .
4 CFRelease(s2); 

• __bridge_retained: 解除 ARC 的全部权 

    不是全部的 Objective-c 和 Core Fundation 对象能够免费桥接的。好比,CGImage 和 UIImage 就不能彼此转换,CGColor 和 UIColor 也不行。

咱们已经添加了一个委托协议,它带有一个方法,同时添加了一个遵照该协议的属性。 注意该属性被声明为"weak"。保持委托指针为弱指针是必须的,这样能够防止全部权循环 (ownership cycles)。

你应该熟悉保留循环(retain cycle)的概念,两个对象互相保留,这会致使他们都得不到 释放。这是内存泄漏的常见形式。在实现垃圾回收(GC)处理内存管理的系统中,垃圾搜 集器能够识别这种循环,并会释放他们。可是 ARC 不是垃圾回收,仍然须要你本身处理所 有权循环。弱指针是打断这类循环的重要工具。 

    除了 strong 和 weak,还有一个新的修饰符,unsafe_unretained。通常状况下你不会用到 它。编译器不会为被声明为 unsafe_unretained 的变量或属性自动添加 retain 或 release。

这个新修饰符采用"unsafe"来组成它的名字,这是由于它能够指向一个再也不存在的对 象。若是你试着使用那种指针,你的程序极有可能崩溃。这是须要用 NSZombieEnabled 调 试工具来找出的问题。技术上讲,若是你不使用任何 unsafe_unretained 属性或变量,你毫不 会再向已经被释放的对象发送消息。

大多数时间里,你都但愿用 strong,有时也用 weak,几乎不会用 unsafe_unretained。 unsafe_unretained 仍然还存在的缘由,是为了兼容 iOS4,在 iOS4 中弱指针系统不可用,另 外的缘由是为了一些其余的技巧。 

相关文章
相关标签/搜索