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 中弱指針系統不可用,另 外的緣由是爲了一些其餘的技巧。 

相關文章
相關標籤/搜索