日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(译)在Objective-c里面使用property教程

發布時間:2024/4/17 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (译)在Objective-c里面使用property教程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

免責申明(必讀!):本博客提供的所有教程的翻譯原稿均來自于互聯網,僅供學習交流之用,切勿進行商業傳播。同時,轉載時不要移除本申明。如產生任何糾紛,均與本博客所有人、發表該翻譯稿之人無任何關系。謝謝合作!

原文鏈接地址:http://www.raywenderlich.com/2712/using-properties-in-objective-c-tutorial

教程截圖:

  這是在iphone上面使用objc,與內存管理有關的第三篇教程。

  在第一篇教程中,我們介紹了在objective-c里面如果使用實例變量和引用計數來管理內存。

  在第二篇教程中,我們介紹了如何檢測內存泄露、與內存有關的易犯的錯誤,使用是Instruments以及其它輔助工具。

  在這第三篇教程中,也是本系列的最后一篇教程,我們將談一談objc的property。我們將介紹property是什么,它是怎樣工作的,有一些什么樣的規則,以及使用它們可以用來避免大部分與內存相關的問題。

  如果你還沒有本系列教程的樣例工程的話,可以點擊這里下載,我們將從這個工程開始。

Retain Your Memory

  讓我們先回顧一下,本項目需要管理內存的地方在哪。

  目前RootViewController有兩個實例變量:_sushiTypes, 和 _lastSushiSelected.

@interface RootViewController : UITableViewController {
NSArray
* _sushiTypes;
NSString
* _lastSushiSelected;
}

@end

  對于_sushiTypes,我們是在viewDidLoad里面通過alloc/init的方式來創建的,然后在viewDidUnload和dealloc里面release。

// In viewDidLoad. Afterwards, retain count is 1.
_sushiTypes = [[NSArray alloc] initWithObjects:@"California Roll",
@"Tuna Roll", @"Salmon Roll", @"Unagi Roll",
@"Philadelphia Roll", @"Rainbow Roll",
@"Vegetable Roll", @"Spider Roll",
@"Shrimp Tempura Roll", @"Cucumber Roll",
@"Yellowtail Roll", @"Spicy Tuna Roll",
@"Avocado Roll", @"Scallop Roll",
nil];

// In viewDidUnload and dealloc. Afterwards, retain count is 0.
[_sushiTypes release];
_sushiTypes
= nil;

  對于_lastSushiSelected,它是在用戶選中table view的一行時被賦值的。它在兩個地方有release。一個是在賦值之前,還有一個是在dealloc時面,請看下面代碼:

[_lastSushiSelected release];
_lastSushiSelected
= [sushiString retain];

// In dealloc
[_sushiTypes release];
_sushiTypes
= nil;

  這種方法肯定是可行的,但是,它需要你很認真的思考,每一次你給一個變量賦值的時候,都要認真考慮與之相關的內存問題。要不要先release后再賦值啊,要不要retain啊,總之,當變量一多,項目一大起來,各種內存問題就隨之而來了。

  因此,接下來,我會向你介紹一種簡單的方法---使用property來管理內存。

搬把椅子過來,開始編碼吧

  如果你熟悉其它編程語言,比如java或者c#,于對getters和setters的概念肯定不陌生。當你擁有一個_sushiTypes的實例變量的時候,你經常需要讓其它類的對象來訪問這個變量。但是,如果直接使用.號的方式去訪問不太好,它破壞了封裝性的原則,把類的實現爆露給外面的,編程大師說的。不管你信不信,反正我是信了。:)

  因此,你需要一個方法,叫做?“getSushiTypes”(或者僅僅是?“sushiTypes” ,這樣少打了3個字母),同時,還需要一個方法,叫做“setSushiTypes”.通過使用這兩個方法來訪問類的實例變量。這是一個好的編碼習慣,因為你可以改變實例變量的名字,但是你不會影響到其它類,因為接口沒變。所以,我們編碼代碼的時候,也要多針對接口編碼,少針對實現編碼。當然,使用getter和setter還有其它好處,你可以在里面用NSLog輸出一些內容,這樣你就知道有沒有人想窺探你的私有變量啦。相當于一個保鏢。

  像上面我所說的那樣,為每個的的實例變量定義相應的getter和setter方法,當然,前提是你想讓外部訪問這個變量你才定義,你別搞得把全部變量都公開,那樣封裝的意義在哪里呢?這樣,將會使內存管理的工作變得輕松。接下來,讓我們看看,我是如何給這兩個變量添加getters和setters的。

  首先,在RootViewController.h里面,聲明下面四個方法:

- (NSArray *)sushiTypes;
- (void)setSushiTypes:(NSArray *)sushiTypes;
- (NSString *)lastSushiSelected;
- (void)setLastSushiSelected:(NSString *)lastSushiSelected;

  然后,在RootViewController.m底部添加其實現:

- (NSArray *)sushiTypes {
return _sushiTypes;
}

- (void)setSushiTypes:(NSArray *)sushiTypes {
[sushiTypes retain];
[_sushiTypes release];
_sushiTypes
= sushiTypes;
}

- (NSString *)lastSushiSelected {
return _lastSushiSelected;
}

- (void)setLastSushiSelected:(NSString *)lastSushiSelected {
[lastSushiSelected retain];
[_lastSushiSelected release];
_lastSushiSelected
= lastSushiSelected;
}

  這里的getter方法很簡單,它們只是返回各自的變量而已。

  而setter方法,首先把傳入的參數引用計數加1,同時把之前的實例變量引用計數減1,然后再把輸入的變量賦值給實例變量。(譯者:這里的寫法其實不好,沒有考慮自賦值的情況。如果大家也過C++的String類,那么寫拷貝構造函數和賦值操作符的時候,是一定要考慮自賦值的情況的,不然會出問題。但是,上面作者的寫法不會有問題。因為它先retain的,后release的。如果你寫反了,先release,那么就出問題了。但是,如果我考慮自賦值的情況,那么我就不用考慮這種先后順序的問題了。具體寫法請參照我的原創,objc @property詳解)。通過這種方式,新傳入的參數被實例變量所引用,因為是所有者,所以符合“誰擁有,誰retain”的原則。

  你可能會奇怪,為什么setter方法要先調用retain/release,然后再賦值,而且順序不能變。當然啦,肯定是防止自賦值的情況啦。如果你還是搞不懂,那就算了吧,有些事情,總有一天你會明白的。:)

  注意,這里為什么要把實例變量的命名前面加一個下劃線呢?這樣做一是可以使得getter和setter方法的參數命名獲得方便。如果我們的實例變量命名為?“sushiTypes”,那么我們的setSushiTypes函數的參數名就不能再是“sushiTypes”,因為那會引起沖突,編譯會報錯的。同時,如果你把所有的實例變量都加一個下劃線,你的同事看你的代碼的時候,也馬上就知道,這是一個實例變量,我使用時得小心。當然,還有apple的kvc和kvo機制,也靠下劃線去搜索key,具體我不展開說了,看書吧。

  最后,注意,這里的getter和setter方法不是線程安全的,但是,對于本應用程序來說,getter和setter方法只會在主線程里面訪問,所以“線程安全不安全”,跟咱沒關系!

現在,你地基有了,開干吧!

  現在,你有新的getter和setter了,修改本類中的其它代碼,開始使用getter和setter吧。讓我們先從sushiTypes開始:

// In viewDidLoad
self.sushiTypes = [[[NSArray alloc] initWithObjects:@"California Roll",
@"Tuna Roll", @"Salmon Roll", @"Unagi Roll",
@"Philadelphia Roll", @"Rainbow Roll",
@"Vegetable Roll", @"Spider Roll",
@"Shrimp Tempura Roll", @"Cucumber Roll",
@"Yellowtail Roll", @"Spicy Tuna Roll",
@"Avocado Roll", @"Scallop Roll",
nil] autorelease];

// In viewDidUnload and dealloc
self.sushiTypes = nil;

  調用“self.sushiTypes = xxx”和調用?“[self setSushiTypes:xxx]”,這兩者完全等價--這里的“.”號,對于我來說,就是“好看”而已。

  因此,我們不是去直接訪問_sushiTypes實例變量,而是用setter來設置它的值。回想一下,setter會把傳入的對數引用計數加1.因此,現在,我們不能直接把alloc/init得到的值直接賦給_sushiTypes了(因為,我們通過setter訪問的時候,那這個alloc/init創建的變量的引用計數會是2,那會有問題,因為它的所有者只有一個,那意味著,在將來,只會被所有者release一次。但是,此時引用計數還是1,永遠也不會被釋放掉了。也就是說,恭喜你!內存泄露了!)所以,在調用alloc/init之后,我們還需要調用一下autorelease。

  在viewDidUnload和dealloc方法里同,我們不再是手動地relase再設置為nil了。我們只需要使用setter,一句設計self.xxx = nil就搞定。如果你用self._sushiTypes = nil的話,那么會生成下列的代碼:

[nil retain]; // Does nothing
[_sushiTypes release];
_sushiTypes
= nil;

  順便說一下,給您提個醒----一些人可能跟你說過“永遠不要在init或者dealloc方法里面使用getter或者setter方法”。他們這樣說是為什么呢?因為,如果你在alloc或者dealloc函數里面使用setter或getter,但是,它的子類重寫了getter和setter,因為objc所有的方法都是“虛方法”,也就是說可以被重寫。那么子類init方法調用[(self = [super init]))的時候,先調父類的init方法,而里面使用了getter和setter,而正好這兩個方法又被你覆蓋了,如果你在這兩個覆蓋的方法里面干了一些事,那么就會有問題了。仔細想想,為什么!因為,你的子類還沒有初使化完畢啊!!!你現在還在調用父類的“構造函數”,但是,你已經使用了子類的方法!!!但是,我想說的是,我在這里違反了某些人提供的原則。為什么,因為我知道會可能有副作用。所以,我不會輕易重載父類的getter或setter方法。我們這里這樣寫,可以幫助我們簡化代碼。這當然是我個人意見,僅供參考。

  現在,修改代碼,讓lastSushiSelected也使用setter方法來賦值:

self.lastSushiSelected = sushiString;

// In dealloc
self.lastSushiSelected = nil;

  哇---現在,我們對于內存問題的擔心少了很多了,不是嗎?你沒有開動腦筋使勁想,哪里需要retain啊,哪些需要release啊。這里的setter方法,它在某種程度上替你完成了內存管理的工作。

一個簡單的建議

  因此,寫getter和setter可以方便其它類訪問你類里面的實例變量,同時,有時候也會使你的內存管理工作變得更加輕松。

  但是,一遍又一遍地寫一大堆這些getter和setter方法,那么我會瘋掉的。搞java的為什么沒瘋?因為eclipse自動可以生成。搞c++的為什么也沒瘋,因為,可以直接public。但是,objc 的@public是沒用的。不用擔心,沒有人會想一遍又一遍地干重復的事情的,所以objc2.0提供了一個新的,非常有用的特性,叫做@property,也叫屬性。

  我們可以把我們前面寫的那些getter和setter方法全部注釋掉,只需要寫上下面兩行代碼就夠了。自己動手試一下吧,打開RootViewController.h,然后找到getter和setter聲明的地方,把它換成下面的2行代碼:

@property (nonatomic, retain) NSArray * sushiTypes;
@property (nonatomic, retain) NSString
* lastSushiSelected;

  這是使用屬性的第一步,創建屬性聲明。

  屬性的聲明以@property關鍵字開始,然后在括號里面傳入一些參數(比如atomic/nonatomic/assign/copy/retain等)。最后,你指明了屬性的類型和名字。

  propetyceov是一些特殊的關鍵字,它可以告訴編譯器如何生成getter和setter。這里,你指定了兩個參數,一個是nonatomic,它是告訴編譯器,你不用擔心多線程的問題。還有一個是reatin,它是告訴編譯器,在把setter參數傳給實例變量之前,要先retain一下。

  在其它情況下,你可能想使用“assign”參數,而不是reatin,“assign”告訴編譯器不要retain傳入的參數。或者,有時你還需要指定“copy”參數,它會在setter參數賦值給實例變量之前,先copy一下。

  好了,為了完成property的使用,你先轉到RootViewController.m,刪除之前寫的getter和setter,然后在文件的頂部添加下面2行代碼:

@synthesize sushiTypes = _sushiTypes;
@synthesize lastSushiSelected
= _lastSushiSelected;

  上面的代碼是告訴編譯器,請你基于我前面定義的property及其參數,請為我生成相應的getter和setter方法。你使用@synthesize關鍵字開始,然后給出屬性名字,(如果屬性名字和實例變量名字不一樣的話),那么一定要寫上=于號,這樣在生成setter方法的時候,編譯才知道,傳入的參數要賦值給誰。切記一次要寫上等于號!!!如果實例變量名和屬性名一樣,那就沒必須了。

  就這么多!編譯并運行代碼吧,一樣很ok,運行得很好。但是,和之前你寫的那堆代碼相比較,是不是更容易理解了呢?同時也會使得出錯的概率下降。

  到目前為止,你應該了角propety的用法,以及具體是如何工作的吧!接下來,我將給出一些使用property的建議。

一般性的策略

  我想在這篇教程里面添加一些這樣的策略,因為,它能夠幫助我在管理objc內存的時候,更加輕松,更加不容易犯錯誤。

  如果你遵守這些規則的話,那么,在大部分時候,你會遠離內存相關問題的煩惱。當然,盲目地背下這些規則而不去理解,為什么會有這些規則,為什么這些規則就能夠有作用。這肯定是不行的啦!但是,如果你是新手的話,你可以按照我給的這些規則去做,這樣你會避免大量的內存相關的錯誤,使得你的日常編程活動更加輕松。

  我先把這些規則一條條列出來,接下來,我再詳細依個討論。

  • 總是為所有的實例變量定義屬性。
  • 如果它是一個類,那么就設定“retain”為屬性參數,否則的話,就設置為assign。
  • 任何時候創建一個類的實例,請使用alloc/init/autorelease?的方式創建。
  • 任何時候,當給一個變量賦值的時候,總是使用e “self.xxx = yyy”。換句話說,就是使用property。
  • 對于你的每一個實例變量,在dealloc函數里面調用?“self.xxx = nil”。如果是一個outlet的話,那么在viewDidLoad里面創建,要記得在viewDidUnload里面銷毀。
  •   好,現在開始逐條討論!

      規則1:通過為每一個實例變量定義property,你可以讓編譯器為你寫內存相關的代碼。缺點很明顯了,你破壞了類的封裝性,這樣的話,可能會使你的類的耦合度變得更高,更不利用維護和代碼復用。

      規則2:通過把類的property參數指明為retain,那么在任何時候,你可以都可以訪問它們。你們你還保存有它們的一個引用計數,內存不會被釋放掉的,你是擁有者,你負責釋放。

      規則3:當你創建一個類對象的時候,使用alloc/init/autorelease慣用法(就像你之前創建sushiTypes的數組那樣的)。這樣的話,內存會被自動釋放。如果你想讓它不釋放的話,那么在要賦值的實例變量,在聲明其property的參數那里,聲明一個retain吧。

      規則4:不管什么時候給實例變量賦值,都使用?self.xxx的語法,這樣的話,當你給實例變量賦值的時候,會先把老的值釋放掉,并且retain新的變量值。注意,有些程序員擔心在init和dealloc函數里面使用getter和setter函數會帶來副作用,但是,我認為這沒什么。只要你對內存管理規則完全清楚,你不會去做“在子類里重寫父類的getter和setter”方法的事的。或者,就算實際需要,必須重寫父類的getter和setter,你也會十分注意,不在重寫的過程中,給代碼帶來任何副作用的,對吧?

      規則5:在dealloc函數里面,使用“self.xxx = nil” ,這樣可以通過property使用其引用計數減1.不用要忘了還有viewDidUnload!

    與cocos2d有關的簡單策略

      我知道,現在我的博客上有一大批cocos2d的忠實粉絲,因此,接下來的tips是特意為你們準備的!

      上面提出的5點規則,對于coocs2d來說,有點太嚴格了,或者直接說,太死了。因為,大部分時候,我們的對象都加到層里面去了,我們在類里面定義一些實例變量,僅僅是為了在除init方法之外的其他方法里面方法使用。(其實,很多人喜歡定義tag,然后在addChild的時候指定一個tag,然后在其他方法里同,使用[self getChildByTag:xxx]來獲得你想要的對象。因為層里面有個CCArray的數組,它用來保存層的所有孩子結點,當調用addChild的時候,其實是調用CCArray的addObject方法,所以,加到層里面的孩子,其引用計數會加1.

      因此,為了避免定義一些不必要的property,下面是我對于cocos2d的使用者的一些建議:

  • 從來不使用property。
  • 把你創建的sprite實例直接賦值給你定義的實例變量。
  • 因為這些精靈都會加到當前層里面去,coocs2d會自動retain,使其引用計數加1.
  • 當你把一個對象從當前層中移除出去的時候,記得把它賦值為nil。
  •   我個人覺得用上面4條方法來開發cocos2d游戲,感覺還不錯,簡單,快捷。

      注意,如果某個對象沒有加到當前層里面去,比如action。那么,就不會使用上面4個規則了。手動去reatin/release吧。

      記住,規則是死的,人是活的!只要理解了objc的內存管理規則,你可以忘記上面所有的規則!

    何去何從?

      這里有本教程的完整源代碼。

      如果你還對property或者內存管理方面有任何疑問的話,請留言。當然,如果各位看觀有什么好的,關于內存管理的小訣竅,小技巧,也歡迎提出來,分享一下,在下十分感激!

      目前為止,關于objc的內存管理系列教程就全部結束啦。真心希望,通過翻譯這3篇教程,以及我自己寫的那一篇教程,能夠幫助大家走出objc內存管理的泥潭。

      如果有什么好的意見或者建議,請在下方留言。如果您想希望得到哪方面的教程,或者你對哪方面還不太熟悉,也請留言。雖然我很忙(其實大家都很忙:)),但是,我有時間的時候,還是會盡力滿足大家的要求的。

      再次謝謝您的閱讀,下篇教程見!

    著作權聲明:本文由http://www.cnblogs.com/andyque翻譯,歡迎轉載分享。請尊重作者勞動,轉載時保留該聲明和作者博客鏈接,謝謝!

    轉載于:https://www.cnblogs.com/zilongshanren/archive/2011/08/08/2123993.html

    總結

    以上是生活随笔為你收集整理的(译)在Objective-c里面使用property教程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。