久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

ARC

 現(xiàn)在決定明天 2014-03-01

本文部分實例取自iOS 5 Toturail一書中關(guān)于ARC的教程和公開內(nèi)容,,僅用于技術(shù)交流和討論,。請不要將本文的部分或全部內(nèi)容用于商用,謝謝合作,。

歡迎轉(zhuǎn)載本文,,但是轉(zhuǎn)載請注明本文出處:http://www./2012/06/arc-hand-by-hand/

本文適合人群:對iOS開發(fā)有一定基礎(chǔ),熟悉iOS開發(fā)中內(nèi)存管理的Reference Counting機制,,對ARC機制有聽聞很向往但是一直由于種種原因沒有使用的童鞋,。本文將從ARC機理入手對這個解放廣大iOS開發(fā)者的偉大機制進行一個剖析,,并逐步引導(dǎo)你開始使用ARC。一旦習(xí)慣ARC,,你一定會被它的間接高效所征服,。

寫在開頭

雖然距離WWDC2011和iOS 5已經(jīng)快一年時間,,但是很多開發(fā)者并沒有利用新方法來提高自己的水平,,這點在ARC的使用上非常明顯(特別是國內(nèi),基本很少見到同行轉(zhuǎn)向ARC),。我曾經(jīng)詢問過一些同行為什么不轉(zhuǎn)向使用ARC,,很多人的回答是擔(dān)心內(nèi)存管理不受自己控制..其實我個人認(rèn)為這是對于ARC機制了解不足從而不自信,所導(dǎo)致的對新事物的恐懼,。而作為最需要“追趕時髦”的職業(yè),,這樣的心態(tài)將相當(dāng)不利。謹(jǐn)以此文希望能清楚表述ARC的機理和用法,,也希望能夠成為現(xiàn)在中文入門教學(xué)缺失的補充,。

什么是ARC

Automatic Reference Counting,自動引用計數(shù),,即ARC,,可以說是WWDC2011和iOS5所引入的最大的變革和最激動人心的變化。ARC是新的LLVM 3.0編譯器的一項特性,,使用ARC,,可以說一舉解決了廣大iOS開發(fā)者所憎恨的手動內(nèi)存管理的麻煩。

在工程中使用ARC非常簡單:只需要像往常那樣編寫代碼,,只不過永遠(yuǎn)不寫retain,release和autorelease三個關(guān)鍵字就好~這是ARC的基本原則,。當(dāng)ARC開啟時,編譯器將自動在代碼合適的地方插入retain, release和autorelease,,而作為開發(fā)者,,完全不需要擔(dān)心編譯器會做錯(除非開發(fā)者自己錯用ARC了)。好了,,ARC相當(dāng)簡單吧~到此為止,,本教程結(jié)束。

等等…也許還有其他問題,,最嚴(yán)重的問題是“我怎么確定讓ARC來管理不會出問題,?”或者“用ARC會讓程序性能下降吧”。對于ARC不能正處理內(nèi)存管理的質(zhì)疑自從ARC出生以來就一直存在,,而現(xiàn)在越來越多的代碼轉(zhuǎn)向ARC并取得了很好的效果,,這證明了ARC是一套有效的簡化開發(fā)復(fù)雜程度的機制,另外通過研究ARC的原理,,可以知道使用ARC甚至能提高程序的效率,。在接下來將詳細(xì)解釋ARC的運行機理并且提供了一個step-by-step的教程,,將非ARC的程序轉(zhuǎn)換為ARC。

ARC工作原理

手動內(nèi)存管理的機理大家應(yīng)該已經(jīng)非常清楚了,,簡單來說,,只要遵循以下三點就可以在手動內(nèi)存管理中避免絕大部分的麻煩:

如果需要持有一個對象,那么對其發(fā)送retain 如果之后不再使用該對象,,那么需要對其發(fā)送release(或者autorealse) 每一次對retain,alloc或者new的調(diào)用,,需要對應(yīng)一次release或autorealse調(diào)用

初學(xué)者可能僅僅只是知道這些規(guī)則,但是在實際使用時難免犯錯,。但是當(dāng)開發(fā)者經(jīng)常使用手動引用計數(shù) Manual Referecen Counting(MRC)的話,,這些規(guī)則將逐漸變?yōu)楸灸堋D銜l(fā)現(xiàn)少一個release的代碼怎么看怎么別扭,,從而減少或者杜絕內(nèi)存管理的錯誤,。可以說MRC的規(guī)則非常簡單,,但是同時也非常容易出錯,。往往很小的錯誤就將引起crash或者OOM之類的嚴(yán)重問題。

在MRC的年代里,,為了避免不小心忘寫release,,Xcode提供了一個很實用的小工具來幫助可能存在的代碼問題(Xcode3里默認(rèn)快捷鍵Shift+A?不記得了),,可以指出潛在的內(nèi)存泄露或者過多釋放,。而ARC在此基礎(chǔ)上更進一步:ARC是Objective-C編譯器的特性,而不是運行時特性或者垃圾回收機制,,ARC所做的只不過是在代碼編譯時為你自動在合適的位置插入release或autorelease,,就如同之前MRC時你所做的那樣。因此,,至少在效率上ARC機制是不會比MRC弱的,,而因為可以在最合適的地方完成引用計數(shù)的維護,以及部分優(yōu)化,,使用ARC甚至能比MRC取得更高的運行效率,。

ARC機制

學(xué)習(xí)ARC很簡單,在MRC時代你需要自己retain一個想要保持的對象,,而現(xiàn)在不需要了?,F(xiàn)在唯一要做的是用一個指針指向這個對象,只要指針沒有被置空,,對象就會一直保持在堆上,。當(dāng)將指針指向新值時,原來的對象會被release一次,。這對實例變量,,sunthesize的變量或者局部變量都是適用的,。比如

1
NSString*firstName=self.textField.text;

firstName現(xiàn)在指向NSString對象,這時這個對象(textField的內(nèi)容字符串)將被hold住,。比如用字符串@“OneV”作為例子,,這個時候firstName持有了@”O(jiān)neV”。

當(dāng)然,,一個對象可以擁有不止一個的持有者(這個類似MRC中的retainCount>1的情況),。在這個例子中顯然self.textField.text也是@“OneV”,那么現(xiàn)在有兩個指針指向?qū)ο驚”O(jiān)neV”(被持有兩次,,retainCount=2,,其實對NSString對象說retainCount是有問題的,,不過anyway~就這個意思而已.),。

過了一會兒,也許用戶在textField里輸入了其他的東西,,那么self.textField.text指針顯然現(xiàn)在指向了別的字符串,,比如@“onevcat”,但是這時候原來的對象已然是存在的,,因為還有一個指針firstName持有它?,F(xiàn)在指針的指向關(guān)系是這樣的:

只有當(dāng)firstName也被設(shè)定了新的值,或者是超出了作用范圍的空間(比如它是局部變量但是這個方法執(zhí)行完了或者它是實例變量但是這個實例被銷毀了),,那么此時firstName也不再持有@“OneV”,,此時不再有指針指向@”O(jiān)neV”,在ARC下這種狀況發(fā)生后對象@”O(jiān)neV”即被銷毀,,內(nèi)存釋放,。

類似于firstName和self.textField.text這樣的指針使用關(guān)鍵字”strong”進行標(biāo)志,它意味著只要該指針指向某個對象,,那么這個對象就不會被銷毀,。反過來說,ARC的一個基本規(guī)則即使,,只要某個對象被任一strong指針指向,,那么它將不會被銷毀。如果對象沒有被任何strong指針指向,,那么就將被銷毀,。在默認(rèn)情況下,所有的實例變量和局部變量都是strong類型的,??梢哉fstrong類型的指針在行為上和MRC時代retain的property是比較相似的。

既然有”strong”,,那肯定有”weak”咯~weak類型的指針也可以指向?qū)ο?,但是并不會持有該對象,。比如?/p>

1
__weak NSString*weakName=self.textField.text

得到的指向關(guān)系是:

這里聲明了一個weak的指針weakName,它并不持有@“onevcat”,。如果self.textField.text的內(nèi)容發(fā)生改變的話,,根據(jù)之前提到的“只要某個對象被任一strong指針指向,那么它將不會被銷毀,。如果對象沒有被任何strong指針指向,,那么就將被銷毀”原則,此時指向@“onevcat”的指針中沒有strong類型的指針,,@”onevcat”將被銷毀,。同時,在ARC機制作用下,,所有指向這個對象的weak指針將被置為nil,。這個特性相當(dāng)有用,相信無數(shù)的開發(fā)者都曾經(jīng)被指針指向已釋放對象所造成的EXC_BAD_ACCESS困擾過,,使用ARC以后,,不論是strong還是weak類型的指針,都不再會指向一個dealloced的對象,,從根源上解決了意外釋放導(dǎo)致的crash,。

不過在大部分情況下,weak類型的指針可能并不會很常用,。比較常見的用法是在兩個對象間存在包含關(guān)系時:對象1有一個strong指針指向?qū)ο?,,并持有它,而對象2中只有一個weak指針指回對象1,,從而避免了循環(huán)持有,。一個常見的例子就是oc中常見的delegate設(shè)計模式,viewController中有一個strong指針指向它所負(fù)責(zé)管理的UITableView,,而UITableView中的dataSource和delegate指針都是指向viewController的weak指針,。可以說,,weak指針的行為和MRC時代的assign有一些相似點,,但是考慮到weak指針更聰明些(會自動指向nil),因此還是有所不同的,。細(xì)節(jié)的東西我們稍后再說,。

注意類似下面的代碼似乎是沒有什么意義的:

1
2
__weak NSString*str=[[NSString alloc]initWithFormat:];
NSLog(@"%@",str);//輸出是"(null)"

由于str是weak,它不會持有alloc出來的NSString對象,,因此這個對象由于沒有有效的strong指針指向,,所以在生成的同時就被銷毀了。如果我們在Xcode中寫了上面的代碼,,我們應(yīng)該會得到一個警告,,因為無論何時這種情況似乎都是不太可能出現(xiàn)的,。你可以把weak換成strong來消除警告,或者直接前面什么都不寫,,因為ARC中默認(rèn)的指針類型就是strong,。

property也可以用strong或weak來標(biāo)記,簡單地把原來寫retain和assign的地方替換成strong或者weak就可以了,。

1
2
@property(nonatomic,strong)NSString*firstName;
@property(nonatomic,weak)id  delegate;

ARC可以為開發(fā)者節(jié)省很多代碼,,使用ARC以后再也不需要關(guān)心什么時候retain,什么時候release,,但是這并不意味你可以不思考內(nèi)存管理,,你可能需要經(jīng)常性地問自己這個問題:誰持有這個對象?

比如下面的代碼,,假設(shè)array是一個NSMutableArray并且里面至少有一個對象:

1
2
3
idobj=[arrayobjectAtIndex:0]; 
[arrayremoveObjectAtIndex:0]; 
NSLog(@"%@",obj);

在MRC時代這幾行代碼應(yīng)該就掛掉了,,因為array中0號對象被remove以后就被立即銷毀了,因此obj指向了一個dealloced的對象,,因此在NSLog的時候?qū)⒊霈F(xiàn)EXC_BAD_ACCESS,。而在ARC中由于obj是strong的,因此它持有了array中的首個對象,,array不再是該對象的唯一持有者。即使我們從array中將obj移除了,,它也依然被別的指針持有,,因此不會被銷毀。

一點提醒

ARC也有一些缺點,,對于初學(xué)者來說,,可能僅只能將ARC用在objective-c對象上(也即繼承自NSObject的對象),但是如果涉及到較為底層的東西,,比如Core Foundation中的malloc()或者free()等,,ARC就鞭長莫及了,這時候還是需要自己手動進行內(nèi)存管理,。在之后我們會看到一些這方面的例子,。另外為了確保ARC能正確的工作,有些語法規(guī)則也會因為ARC而變得稍微嚴(yán)格一些,。

ARC確實可以在適當(dāng)?shù)牡胤綖榇a添加retain或者release,,但是這并不意味著你可以完全忘記內(nèi)存管理,因為你必須在合適的地方把strong指針手動設(shè)置到nil,,否則app很可能會oom,。簡單說還是那句話,你必須時刻清醒誰持有了哪些對象,,而這些持有者在什么時候應(yīng)該變?yōu)橹赶騨il,。

ARC必然是Objective-C以及Apple開發(fā)的趨勢,,今后也會有越來越多的項目采用ARC(甚至不排除MRC在未來某個版本被棄用的可能),Apple也一直鼓勵開發(fā)者開始使用ARC,,因為它確實可以簡化代碼并增強其穩(wěn)定性,。可以這么說,,使用ARC之后,,由于內(nèi)存問題造成的crash基本就是過去式了(OOM除外 :P )

我們正處于由MRC向ARC轉(zhuǎn)變的節(jié)點上,因此可能有時候我們需要在ARC和MRC的代碼間來回切換和適配,。Apple也想到了這一點,,因此為開發(fā)這提供了一些ARC和非ARC代碼混編的機制,這些也將在之后的例子中列出,。另外ARC甚至可以用在C++的代碼中,,而通過遵守一些代碼規(guī)則,iOS 4里也可以使用ARC(雖然我個人認(rèn)為在現(xiàn)在iOS 6都呼之欲出的年代已經(jīng)基本沒有需要為iOS 4做適配的必要了),、

總之,,聰明的開發(fā)者總會嘗試盡可能的自動化流程,已減輕自己的工作負(fù)擔(dān),,而ARC恰恰就為我們提供了這樣的好處:自動幫我們完成了很多以前需要手動完成的工作,,因此對我來說,轉(zhuǎn)向ARC是一件不需要考慮的事情,。

具體操作

說了這么多,,終于可以實踐一下了。在決定使用ARC后,,很多開發(fā)者面臨的首要問題是不知如何下手,。因為可能手上的項目已經(jīng)用MRC寫了一部分,不想麻煩做轉(zhuǎn)變,;或者因為新項目里用ARC時遇到了奇怪的問題,,從而放棄ARC退回MRC。這都是常見的問題,,而在下面,,將通過一個demo引導(dǎo)大家徹底轉(zhuǎn)向ARC的世界。

Demo

例子很簡單,,這是一個查找歌手的應(yīng)用,,包含一個簡單的UITableView和一個搜索框,當(dāng)用戶在搜索框搜索時,,調(diào)用MusicBrainz的API完成名字搜索和匹配,。MusicBrainz是一個開放的音樂信息平臺,它提供了一個免費的XML網(wǎng)頁服務(wù),如果對MusicBrainz比較有興趣的話,,可以到它的官網(wǎng)逛一逛,。

Demo的起始例子可以從這里下載,為了照顧新人,,在這邊進行簡單說明,。在Xcode中打開下載的例子,應(yīng)該可以看到如下內(nèi)容(Xcode和iOS開發(fā)熟練者請?zhí)^此段)

AppDelegate.h/m 這是整個app的delegate,,沒什么特殊的,,每個iOS/Mac程序在main函數(shù)以后的入口,由此進入app的生命周期,。在這里加載了最初的viewController并將其放到Window中展示出來,。另外appDelegate還負(fù)責(zé)處理程序開始退出等系統(tǒng)委托的事件

MainViewController.h/m/xib 這個demo最主要的ViewController,含有一個TableView和一個搜索條,。 SoundEffect.h/m 簡單的播放聲音的類,,在MusicBrainz搜索完畢時播放一個音效。 main.m 程序入口,,所有c程序都從main函數(shù)開始執(zhí)行

AFHTTPRequestOperation.h/m 這是有名的網(wǎng)絡(luò)框架AFNetworking的一部分,,用來幫助等簡單地處理web服務(wù)請求。這里只包含了這一個類而沒有將全部的AFNetworking包括進來,,因為我們只用了這一個類,。完整的框架代碼可以在github的相關(guān)頁面上找到https://github.com/gowalla/AFNetworking

SVProgresHUD.h/m/bundle 是一個常用的進度條指示,當(dāng)搜索的時候出現(xiàn)以提示用戶正在搜索請稍后,。bundle是資源包,,里面包含了幾張該類用到的圖片,打進bundle包的目的一方面是為了資源容易管理,,另一方面也是主要方面時為了不和其他資源發(fā)生沖突(Xcode中資源名字是資源的唯一標(biāo)識,同名字的資源只能出現(xiàn)一次,,而放到bundle包里可以避免這個潛在的問題),。SVProgresHUD可以在這里找到https://github.com/samvermette/SVProgressHUD

快速過一遍這個應(yīng)用吧:MainViewController是UIViewController的子類,對應(yīng)的xib文件定義了對應(yīng)的UITableView和UISearchBar,。TableView中顯示searchResult數(shù)組中的內(nèi)容,。當(dāng)用戶搜索時,用AFHTTPRequestOperation發(fā)一個HTTP請求,,當(dāng)從MusicBrainz得到回應(yīng)后將結(jié)果放入searchResult數(shù)組中并用tableView顯示,,當(dāng)返回結(jié)果是空時在tableView中顯示沒找到。主要的邏輯都在MainViewController.m中的-searchBarSearchButtonClicked:方法中,,生成了用于查詢的URL,,根據(jù)MusicBrainz的需求替換了請求的header,并且完成了返回邏輯,然后在主線程中刷新UI,。整個程序還是比較簡單的~

MRC到ARC的自動轉(zhuǎn)換

回到正題,,我們討論的是ARC,關(guān)于REST API和XML解析的技術(shù)細(xì)節(jié)就暫時先忽略吧..整個程序都是用MRC來進行內(nèi)存管理的,,首先來讓我們把這個demo轉(zhuǎn)成ARC吧,。基本上轉(zhuǎn)換為ARC意味著把所有的retain,release和autorelease關(guān)鍵字去掉,,在之前我們明確幾件事情:
* Xcode提供了一個ARC自動轉(zhuǎn)換工具,,可以幫助你將源碼轉(zhuǎn)為ARC
* 當(dāng)然你也可以自己動手完成ARC轉(zhuǎn)換
* 同時你也可以指定對于某些你不想轉(zhuǎn)換的代碼禁用ARC,這對于很多龐大復(fù)雜的還沒有轉(zhuǎn)至ARC的第三方庫幫助很大,,因為不是你寫的代碼你想動手修改的話代碼超級容易mess…

對于我們的demo,,為了說明問題,這三種策略我們都將采用,,注意這僅僅只是為了展示如何轉(zhuǎn)換,。實際操作中不需要這么麻煩,而且今后的絕大部分情況應(yīng)該是從工程建立開始就是ARC的,。

首先,,ARC是LLVM3.0編譯器的特性,而老的工程特別是Xcode3時代的工程的默認(rèn)編譯器很可能是GCC或者LLVM-GCC,,因此第一步就是確認(rèn)編譯器是否正確,。在Project設(shè)置面板,選擇target,,在Build Settings中將Compiler for C/C++/Objective-C選為Apple LLVMcompiler 3.0或以上,。為了確保之后轉(zhuǎn)換的順利,在這里我個人建議最好把Treat Warnings as Errors和 Run Static Analyzer都打開,,確保在改變編譯器后代碼依舊沒有警告或者內(nèi)存問題(雖然靜態(tài)分析可能不太能保證這一點,,但是聊勝于無)。好了~clean(Shift+Cmd+K)以后Bulid一下試試看,,經(jīng)過修改后的demo工程沒有任何警告和錯誤,,這是很好的開始。(對于存在警告的代碼,,這里是很好的修復(fù)的時機..請在轉(zhuǎn)換前確保原來的代碼沒有內(nèi)存問題),。

接下來就是完成從MRC到ARC的偉大轉(zhuǎn)換了。還是在Build Settings頁面,,把Objective-C Automatic Reference Counting改成YES(如果找不到的話請看一看搜索欄前面的小標(biāo)簽是不是調(diào)成All了..這個選項在Basic里是不出現(xiàn)的),,這樣我們的工程就將在所有源代碼中啟用ARC了。然后…試著編譯一下看看,,嗯..無數(shù)的錯誤,。

這是很正常的,,因為ARC里不允許出現(xiàn)retain,release之類的,而MRC的代碼這些是肯定會有的東西,。我們可以手動一個一個對應(yīng)地去修復(fù)這些錯誤,,但是這很麻煩。Xcode為我們提供了一個自動轉(zhuǎn)換工具,,可以幫助重寫源代碼,,簡單來說就是去掉多余的語句并且重寫一些property關(guān)鍵字。

這個小工具是Edit->Refactor下的Convert to Objective-C ARC,,點擊后會讓我們選擇要轉(zhuǎn)換哪幾個文件,,在這里為了說明除了自動轉(zhuǎn)換外的方法,我們不全部轉(zhuǎn)換,,而只是選取其中幾個轉(zhuǎn)換(MainViewController.m和AFHTTPRequestOperation.m不做轉(zhuǎn)換),。注意到這個對話框上有個警告標(biāo)志告訴我們target已經(jīng)是ARC了,這是由于之前我們在Build Settings里已經(jīng)設(shè)置了啟用ARC,,其實直接在這里做轉(zhuǎn)換后Xcode會自動幫我們開啟ARC,。點擊檢查后,Xcode告訴我們一個不幸的消息,,不能轉(zhuǎn)換,,需要修復(fù)ARC readiness issues..后面還告訴我們要看到所有的所謂的ARC readiness issues,可以到設(shè)置的General里把Continue building after errors勾上…What the f**k…好吧~先乖乖聽從Xcode的建議”Cmd+,“然后Continue building after errors打勾然后再build,。

問題依舊,,不過在issue面板里應(yīng)該可以看到所有出問題的代碼了。在我們的例子里,,問題出在SoundEffect.m里:

1
2
3
4
5
6
7
8
NSURL*fileURL=[[NSBundle mainBundle]URLForResource:filenamewithExtension:nil];
if(fileURL!=nil)
{
    SystemSoundID theSoundID;
    OSStatus error=AudioServicesCreateSystemSoundID((CFURLRef)fileURL,&theSoundID);
    if(error==kAudioServicesNoError)
        soundID=theSoundID;
}

這里代碼嘗試把一個NSURL指針強制轉(zhuǎn)換為一個CFURLRef指針,。這里涉及到一些Core Services特別是Core Foundation(CF)的東西,AudioServicesCreateSystemSoundID()函數(shù)接受CFURLRef為參數(shù),,這是一個CF的概念,,但是我們在較高的抽象層級上所建立的是NSURL對象。在Cocoa框架中,,有很多頂層對象對底層的抽象,,而在使用中我們往往可以不加區(qū)別地對這兩種對象進行同樣的對待,這類對象即為可以”自由橋接”的對象(toll-free bridged),。NSURL和CFURLRef就是一對好基友好例子,在這里其實CFURLRef和NSURL是可以進行替換的,。

通常來說為了代碼在底層級上的正確,,在iOS開發(fā)中對基于C的API的調(diào)用所傳入的參數(shù)一般都是CF對象,而Objective-C的API調(diào)用都是傳入NSObject對象,。因此在采用自由橋接來調(diào)用C API的時候就需要進行轉(zhuǎn)換,。但是在使用ARC編譯的時候,因為內(nèi)存管理的原因,編譯器需要知道對這些橋接對象要實行什么樣的操作,。如果一個NSURL對象替代了CFURLRef,,那么在作用區(qū)域外,,應(yīng)該由誰來決定內(nèi)存釋放和對象銷毀呢,?為了解決這個問題,引入了bridge,bridge_transfer和__bridge_retained三個關(guān)鍵字,。關(guān)于選取哪個關(guān)鍵字做轉(zhuǎn)換,,需要由實際的代碼行為來決定,。如果對于自由橋接機制感興趣,大家可以自己找找的相關(guān)內(nèi)容,,比如適用類型,、內(nèi)部機制一個簡介~之后我也會對這個問題做進一步說明

回到demo,我們現(xiàn)在在上面的代碼中加上__bridge進行轉(zhuǎn)換,。然后再運行ARC轉(zhuǎn)換工具,,這時候檢查應(yīng)該沒有其他問題了,那么讓我們進行轉(zhuǎn)換吧~當(dāng)然在真正轉(zhuǎn)換之前會有一個預(yù)覽界面,,在這里我們最好檢查一下轉(zhuǎn)換是不是都按照預(yù)想進行了..要是出現(xiàn)大面積錯誤又沒有備份或者出現(xiàn)各種意外的話就可以哭了…

前后變化的話比較簡單,,基本就是去掉不需要的代碼和改變property的類型而已,其實有信心的話不太需要每次都看,,但是如果是第一次執(zhí)行ARC轉(zhuǎn)換的操作的話,,我還是建議稍微看一下變化,這樣能對ARC有個直觀上的了解,。檢查一遍,,應(yīng)該沒什么問題了..需要注意的是main.m里關(guān)于autoreleasepool的變化以及所有dealloc調(diào)用里的[super dealloc]的刪除,它們同樣是MRC到ARC的主要變化..

好了~轉(zhuǎn)換完成以后我們再build看看..應(yīng)該會有一些警告,。對于原來retain的property,,比較保險的做法是轉(zhuǎn)為strong,在LLVM3.0中自動轉(zhuǎn)換是這樣做的,,但是在3.1中property默認(rèn)并不是strong,,這樣在使用property賦值時存在警告,我們在property聲明里加上strong就好了~然后就是SVProgressHUD.m里可能存在問題,,這是由于原作者把release的代碼和其他代碼寫在一行了.導(dǎo)致自動轉(zhuǎn)換時只刪掉了部分,,而留下了部分不應(yīng)該存在的代碼,刪掉對變量的空調(diào)用就好了..

自動轉(zhuǎn)換之后的故事

然后再編譯,,沒有任何錯誤和警告了,,好棒~等等…我們剛才沒有對MainViewController和AFHTTPRequestOperation進行處理吧,那么這兩個文件里應(yīng)該還存在release之類的東西吧..,?看一看這兩個文件,,果然有各種release,,但是為什么能編譯通過呢?,!明明剛才在自動轉(zhuǎn)換前他們還有N多錯的嘛…答案很簡單,,在自動轉(zhuǎn)換的時候因為我們沒有勾選這兩個文件,因此編譯器在自動轉(zhuǎn)換過后為這兩個文件標(biāo)記了”不使用ARC編譯”,??梢钥吹皆趖arget的Building Phases下,MainViewController.m和AFHTTPRequestOperation.m兩個文件后面被加上了-fno-objc-arc的編譯標(biāo)記,,被加上該標(biāo)記的文件將不使用ARC規(guī)則進行編譯,。

提供這樣的編譯標(biāo)記的原因是顯而易見的,因為總是有一部分的第三方代碼并沒有轉(zhuǎn)換為ARC(可能是由于維護者犯懶或者已經(jīng)終止維護),,所以對于這部分代碼,,為了迅速完成轉(zhuǎn)換,最好是使用-fno-objc-arc標(biāo)記來禁止在這些源碼上使用ARC,。

為了方便查找,,再此列出一些在轉(zhuǎn)換時可能出現(xiàn)的問題,當(dāng)然在我們使用ARC時也需要注意避免代碼中出現(xiàn)這些問題:

  • “Cast … requires a bridged cast”

這是我們在demo中遇到的問題,,不再贅述

  • Receiver type ‘X’ for instance message is a forward declaration

這往往是引用的問題,。ARC要求完整的前向引用,也就是說在MRC時代可能只需要在.h中申明@class就可以,,但是在ARC中如果調(diào)用某個子類中未覆蓋的父類中的方法的話,,必須對父類.h引用,否則無法編譯,。

  • Switch case is in protected scope

現(xiàn)在switch語句必須加上{}了,,ARC需要知道局部變量的作用域,加上{}后switch語法更加嚴(yán)格,,否則遇到?jīng)]有break的分支的話內(nèi)存管理會出現(xiàn)問題,。

  • A name is referenced outside the NSAutoreleasePool scope that it was declared in

這是由于寫了自己的autoreleasepool,而在轉(zhuǎn)換時在原來的pool中申明的變量在新的@autoreleasepool中作用域?qū)⒈痪窒?。解決方法是把變量申明拿到pool的申請之前,。

  • ARC forbids Objective-C objects in structs or unions

可以說ARC所引入的最嚴(yán)格的限制是不能在C結(jié)構(gòu)體中放OC對象了..因此類似下面這樣的代碼是不可用的

1
2
3
4
typedefstruct{
    UIImage *selectedImage;
    UIImage *disabledImage;
}ButtonImages;

這個問題只有乖乖想辦法了..改變原來的結(jié)構(gòu)什么的..

手動轉(zhuǎn)換

剛才做了對demo的大部分轉(zhuǎn)換,還剩下了MainViewController和AFHTTPRequestOperation是MRC,。但是由于使用了-fno-objc-arc,,因此現(xiàn)在編譯和運行都沒有問題了。下面我們看看如何手動把MainViewController轉(zhuǎn)為ARC,,這也有助于進一步理解ARC的規(guī)則,。

首先,我們需要轉(zhuǎn)變一下觀念…對于MainViewController.h,,在.h中申明了兩個實例變量:

1
2
3
4
5
@interfaceMainViewController :UIViewController  
{
    NSOperationQueue*queue;
    NSMutableString*currentStringValue;
}

我們不妨仔細(xì)考慮一下,,為什么在interface里出現(xiàn)了實例變量的申明?通常來說,,實例變量只是在類的實例中被使用,,而你所寫的類的使用者并沒有太多必要了解你的類中有哪些實例變量。而對于絕大部分的實例變量,,應(yīng)該都是protected或者private的,,對它們的操作只應(yīng)該用setter和getter,而這正是property所要做的工作,??梢哉f,將實例變量寫在頭文件中是一種遺留的陋習(xí),。更好的寫實例變量名字的地方應(yīng)當(dāng)與類實現(xiàn)關(guān)系更為密切,,為了隱藏細(xì)節(jié),我們應(yīng)該考慮將它們寫在@implementation里,。好消息是,,在LLVM3.0中,不論是否開啟ARC,,編譯器是支持將實例變量寫到實現(xiàn)文件中的,。甚至如果沒有特殊需要又用了property,我們都不應(yīng)該寫無意義的實例變量申明,,因為在@synthesize中進行綁定時,,我們就可以設(shè)置變量名字了,這樣寫的話可以讓代碼更加簡潔,。

在這里我們對著兩個實例變量不需要property(外部成員不應(yīng)當(dāng)能訪問到它們),,因此我們把申明移到.m里中。修改后的.h是這樣的,,十分簡潔一看就懂~

1
2
3
4
5
#import 
@interfaceMainViewController :UIViewController
@property(nonatomic,retain)IBOutlet UITableView *tableView;  
@property(nonatomic,retain)IBOutlet UISearchBar *searchBar; 
@end

然后.m的開頭變成這樣:

1
2
3
4
5
@implementationMainViewController
{
    NSOperationQueue*queue; 
    NSMutableString*currentStringValue; 
}

這樣的寫法讓代碼相當(dāng)靈活,,而且不得不承認(rèn).m確實是這些實例變量的應(yīng)該在的地方…build一下,沒問題..當(dāng)然對于SoundEffect類也可以做相似的操作,,這會讓使用你的類的人很開心,,因為.h越簡單越好..P.S.另外一個好處可以減少.h里的引用,減少編譯時間(雖然不明顯=,。=)

然后就可以在MainViewController里啟用ARC了,,方法很簡單,刪掉Build Phases里相關(guān)文件的-fno-objc-arc標(biāo)記就可以了~然后..然后當(dāng)然是一大堆錯誤啦,。我們來手動一個個改吧,,雖然談不上樂趣,但是成功以后也會很有成就~(如果你不幸在啟用ARC后build還是成功了,,恭喜你遇到了Xcode的bug,,請Cmd+Q然后重新打開Xcode把=_=)

dealloc

紅色最密集的地方是dealloc,,因為每一行都是release。由于在這里dealloc并沒有做除了release和super dealloc之外的任何事情,,因此簡單地把整個方法刪掉就好了,。當(dāng)然,在對象被銷毀時,,dealloc還是會被調(diào)用的,,因此我們在需要對非ARC管理的內(nèi)存進行管理和必要的邏輯操作的時候,還是應(yīng)該保留dealloc的,,當(dāng)然這涉及到CF以及以下層的東西:比如對于retain的CF對象要CFRelease(),,對于malloc()到堆上的東西要free()掉,對于添加的observer可以在這里remove,,schedule的timer在這里invalidate等等~[super dealloc]這個消息也不再需要發(fā)了,,ARC會自動幫你搞定。

另外,,在MRC時代一個常做的事情是在dealloc里把指向自己的delegate設(shè)成nil(否則就等著EXC_BAD_ACCESS吧 :P ),,而現(xiàn)在一般delegate都是weak的,因此在self被銷毀后這個指針自動被置成nil了,,你不用再為之擔(dān)心,,好棒啊..

去掉各種release和autorelease

這個很直接,沒有任何問題,。去掉就行了~不再多說

討論一下Property

在MainViewController.m里的類擴展中定義了兩個property:

1
2
3
4
@interfaceMainViewController()
@property(nonatomic,retain)NSMutableArray*searchResults;
@property(nonatomic,retain)SoundEffect *soundEffect; 
@end

申明的類型是retain,,關(guān)于retain,assign和copy的討論已經(jīng)爛大街了,在此不再討論,。在MRC的年代使用property可以幫助我們使用dot notation的時候簡化對象的retain和copy,,而在ARC時代,這就顯得比較多余了,。在我看來,,使用property和點方法來調(diào)用setter和getter是不必要的。property只在將需要的數(shù)據(jù)在.h中暴露給其他類時才需要,,而在本類中,,只需要用實例變量就可以。因此我們可以移去searchResults和soundEffect的@property和@synthesize,,并將起移到實例變量申明中:

1
2
3
4
5
6
7
@implementationMainViewController
{ 
    NSOperationQueue*queue; 
    NSMutableString*currentStringValue;
    NSMutableArray*searchResults;
    SoundEffect *soundEffect; 
}

相應(yīng)地,,我們需要將對應(yīng)的self.searchResult和self.soundEffect的self.都去去掉。在這里需要注意的是,,雖然我們?nèi)サ袅藄oundEffect的property和synthesize,,但是我們依然有一個lazy loading的方法- (SoundEffect *)soundEffect,神奇之處在于(可能你以前也不知道),點方法并不需要@property關(guān)鍵字的支持,,雖然大部分時間是這么用的..(property只是對setter或者getter的申明,,而點方法是對其的調(diào)用,在這個例子的實現(xiàn)中我們事實上實現(xiàn)了-soundEffect這個getter方法,,所以點方法在等號右邊的getter調(diào)用是沒有問題的),。為了避免誤解,建議把self.soundEffect的getter調(diào)用改寫成[self soundEffect],。

然后我們看看.h里的property~里面有兩個retain的IBOutlet。retain關(guān)鍵字在ARC中是依舊可用的,,它在ARC中所扮演的角色和strong完全一樣,。為了避免迷惑,最好在需要的時候?qū)⑵鋵憺閟trong,,那樣更符合ARC的規(guī)則,。對于這兩個property,我們將其申明為weak(事實上,,如果沒有特別意外,,除了最頂層的IBOutlet意外,自己寫的outlet都應(yīng)該是weak),。通過加載xib得到的用戶界面,,在其從xib文件加載時,就已經(jīng)是view hierarchy的一部分了,,而view hierarchy中的指向都是strong的,。因此outlet所指向的UI對象不應(yīng)當(dāng)再被hold一次了。將這些outlet寫為weak的最顯而易見的好處是你就不用再viewDidUnload方法中再將這些outlet設(shè)為nil了(否則就算view被摧毀了,,但是由于這些UI對象還在被outlet指針指向而無法釋放,,代碼簡潔了很多啊..)。

在我們的demo中將IBOutlet的property改為weak并且刪掉viewDidUnload中關(guān)于這兩個IBOutlet的內(nèi)容~

總結(jié)一下新加入的property的關(guān)鍵字類型:

  • strong 和原來的retain比較相似,,strong的property將對應(yīng)__strong的指針,,它將持有所指向的對象

  • weak 不持有所指向的對象,而且當(dāng)所指對象銷毀時能將自己置為nil,,基本所有的outlet都應(yīng)該用weak

  • unsafe_unretained 這就是原來的assign,。當(dāng)需要支持iOS4時需要用到這個關(guān)鍵字

  • copy 和原來基本一樣..copy一個對象并且為其創(chuàng)建一個strong指針

  • assign 對于對象來說應(yīng)該永遠(yuǎn)不用assign了,實在需要的話應(yīng)該用unsafe_unretained代替(基本找不到這種時候,,大部分assign應(yīng)該都被weak替代),。但是對于基本類型比如int,float,BOOL這樣的東西,還是要用assign,。

特別地,,對于NSString對象,在MRC時代很多人喜歡用copy,,而ARC時代一般喜歡用strong…(我也不懂為什么..求指教)

自由橋接的細(xì)節(jié)

MainViewController現(xiàn)在剩下的問題都是橋接轉(zhuǎn)換問題了~有關(guān)橋接的部分有三處:

  • (NSString *)CFURLCreateStringByAddingPercentEscapes(…):CFStringRef至NSString *

  • (CFStringRef)text:NSString *至CFStringRef

  • (CFStringRef)@“!‘();:@&=+$,/?%#[]“:NSString 至CFStringRef

編譯器對前兩個進行了報錯,,最后一個是常量轉(zhuǎn)換不涉及內(nèi)存管理,。

關(guān)于toll-free bridged,如果不進行細(xì)究,,NSString和CFStringRef是一樣的東西,,新建一個CFStringRef可以這么做:

1
CFStringRef s1=[[NSString alloc]initWithFormat:@"Hello, %@!",name];

然后,這里alloc了而s1是一個CF指針,,要釋放的話,,需要這樣:

1
CFRelease(s1);

相似地可以用CFStringRef來轉(zhuǎn)成一個NSString對象(MRC):

1
2
3
4
5
CFStringRef s2=CFStringCreateWithCString(kCFAllocatorDefault,bytes,kCFStringEncodingMacRoman); 
NSString*s3=(NSString*)s2;
// release the object when you're done 
[s3 release];

在ARC中,編譯器需要知道這些指針應(yīng)該由誰來負(fù)責(zé)釋放,,如果把一個NSObject看做是CF對象的話,,那么ARC就不再負(fù)責(zé)它的釋放工作(記住ARC是only for NSObject的)。對于不需要改變持有者的對象,,直接用簡單的bridge就可以了,,比如之前在SoundEffect.m做的轉(zhuǎn)換。在這里對于(CFStringRef)text這個轉(zhuǎn)換,,ARC已經(jīng)負(fù)責(zé)了text這個NSObject的內(nèi)存管理,,因此這里我們需要一個簡單的bridge。而對于CFURLCreateStringByAddingPercentEscapes方法,,方法中的create暗示了這個方法將形成一個新的對象,,如果我們不需要NSString轉(zhuǎn)換,那么為了避免內(nèi)存的問題,,我們需要使用CFRelease來釋放它,。而這里我們需要一個NSString,因此我們需要告訴編譯器接手它的內(nèi)存管理工作,。這里我們使用bridge_transfer關(guān)鍵字,,將內(nèi)存管理權(quán)由CF object移交給NSObject(或者說ARC)。如果這里我們只用bridge的話,,內(nèi)存管理的負(fù)責(zé)人沒有改變,,那么這里就會出現(xiàn)一個內(nèi)存泄露。另外有時候會看到CFBridgingRelease(),,這其實就是transfer cast的內(nèi)聯(lián)寫法..是一樣的東西,。總之,,需要記住的原則是,,當(dāng)在涉及CF層的東西時,如果函數(shù)名中有含有Create, Copy, 或者Retain之一,,就表示返回的對象的retainCount+1了,,對于這樣的對象,最安全的做法是將其放在CFBridgingRelease()里,來平衡retain和release,。

還有一種bridge方式,,__bridge_retained。顧名思義,,這種轉(zhuǎn)換將在轉(zhuǎn)換時將retainCount加1,。和CFBridgingRelease()相似,也有一個內(nèi)聯(lián)方法CFBridgingRetain()來負(fù)責(zé)和CFRelease()進行平衡,。

需要注意的是,,并非所有的CF對象都是自由橋接的,比如Core Graphics中的所有對象都不是自由橋接的(如CGImage和UIImage,,CGColor和UIColor),。另外也不是只有自由橋接對象才能用bridge來橋接,一個很好的特例是void (指向任意對象的指針,,類似id),對于void 和任意對象的轉(zhuǎn)換,,一般使用_bridge,。(這在將ARC運用在Cocos2D中很有用)

終于搞定了

至此整個工程都ARC了~對于AFHTTPRequestOperation這樣的不支持ARC的第三方代碼,我們的選擇一般都是就不使用ARC了(或者等開源社區(qū)的大大們更新ARC適配版本),??梢灶A(yù)見,在近期會有越來越多的代碼轉(zhuǎn)向ARC,,但是也一定會有大量的代碼暫時或者永遠(yuǎn)保持MRC等個,,所以對于這些代碼就不用太糾結(jié)了~

寫在最后

寫了那么多,希望你現(xiàn)在能對ARC有個比較全面的了解和認(rèn)識了,。ARC肯定是以后的趨勢,,也確實能讓代碼量大大降低,減少了很多無意義的重復(fù)工作,,還提高了app的穩(wěn)定性,。但是凡事還是紙上得來終覺淺,希望作為開發(fā)者的你,,在下一個工程中去嘗試用用ARC~相信你會和我一樣,,馬上愛上這種make life easier的方式的~


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多