Objective-C—— @Property詳解


實例變量:
屬性其實說直白點就是 ivar + setter + getter(實例變量+存取方法),不過在OC中屬性多了字面量這一系列特殊關鍵字使得OC屬性有些不同。

成員屬性我們應該都使用過,比如現在定義一個Car類有name和speed成員變量:

#import <Foundation/Foundation.h>

@interface Car : NSObject
{
    @public
    NSString *name;
    NSInteger speed;
}
@end

在OC類的內部有一個偏移量,專門標記成員變量在內存中的所在位置。如果現在在添加一個新的成員變量在name的前面,那么就會出現偏移量整體便宜的問題,現在添加一個price實例:

#import <Foundation/Foundation.h>

@interface Car : NSObject
{
    @public
    NSInteger price;
    NSString *name;
    NSInteger speed;
}
@end

此時偏移量在內存中顯示如下:

Car Car
name price
speed name
  speed

 

 

 

 

可以看到實例偏移量發生了改變,但是OC將實例變量作為一種存儲偏移量所用的“特殊變量”,交個類對象(class object)保管,偏移量會在運行時查找,所以總能正確的找到偏移量。

 

@Property

使用屬性相比成員變量更加抽象,能夠使用setter和getter對變量做更多的處理。

說一下屬性的特性

 

@synthesize關鍵字

該關鍵字指定了屬性的實例變量名稱,並且根據存儲語義(readwrite、readonly)系統自動合成setter和getter方法,當然也可以手寫來覆蓋系統提供的。

@dynamic

該關鍵字告訴編譯器不要為我合成setter和getter方法,這些方法將由我自己實現。當然我們可以不實現這在編譯階段不會出現問題,直到運行時才會檢查是否實現了setter和getter,如果沒有實現就會拋出異常。

例如在CoreData中NSManagedObject子類的所有屬性全部都是dynamic標記的,這是因為子類的某些屬性不是真正的實例變量,而是對應背后的數據庫,對NSManagedObject對象通過是屬性訪問時會自動使用KVC。

 

屬性特性(語義)

屬性的特質分為四類:

1.原子性:

  原子性就是指該屬性是否為同步的,OC中大部分屬性都是nonatomic(非原子性)的,如果不寫nonatomic那么就會是原子性的。理論上來說原子性屬性的讀寫都將會是同步的,但是OC中atomic並不能一定確定屬性為同步的,如果真要進行同步操作,還要用更加深層次的同步鎖API。而且atomic會很影響效率,所以一般都會寫nonatomic。

2.讀/寫權限:

  讀寫為readonly和readwrite兩種,前一種在系統只會合成getter方法,而后一種則會同時生成setter和getter。如果屬性設置為了readonly屬性,那么該屬性是不可以修改的。

3.內存管理語義:

  assign:該方法只會針對“純量類型”(CGFloat或NSInteger等)的簡單賦值操作,id類型也要用assign,所以一般iOS中的代理delegate屬性都會用assign來標示,如:

@property (nonatomic, assign)   id <UITableViewDataSource> dataSource;
@property (nonatomic, assign)   id <UITableViewDelegate>   delegate;

  strong: 使用該特性實例變量在賦值時,會釋放舊值同時設置新值,對對象產生一個強引用,用MRC來說就是引用計數+1。

  weak: 屬性表明了一種”非擁有關系“,既不釋放舊值,也不保留新值。用MRC就是引用計數不變,當指向的對象被釋放時,該屬性自動被設置為nil。這里多說一點,weak的runtime實現是通過hash表完成的,用變量名做鍵,一旦發現屬性所指的對象被釋放了,立刻設置為nil。

  unsafe_unretained:和weak一樣,唯一的區別就是當對象被釋放后,該屬性不會被設置為nil。所以是unsafe的。

  copy:和strong類似,不過該屬性會被復制一個新的副本。很多時使用copy是為了方式Mutable(可變類型)在我們不知道的情況下修改了屬性值,而用copy可以生成一個不可變的副本防止被修改。如果我們自己實現setter方法的話,需要手動copy。

4.方法名:

  getter = <name>

  setter = <name>

  方法名可以修改為我們合成的方法名,可以使存取方法語義更加符合應用場景。

  如果要在其它屬性里面設置屬性的話,還是要符合屬性特性,比如copy的話我們還是要手動copy一下屬性。這里說一下構造方法里需要直接操作實例變量,而不應該調用setter和getter。

 

對象內部盡量直接訪問實例變量

首先說一下構造方法和析構方法中為什么不能使用setter和getter,因為setter和getter是經過我們包裝過的方法,有可能增加一些判斷,而如果子類調用父類的構造方法同時實現了自己的setter和getter,那么很可能就會出現問題。

通過屬性訪問實例變量會使用屬性的字面語義,會使用KVO所以在執行效率上肯定比直接調用實例變量慢,但是通過屬性訪問可以截獲屬性的獲取和設置更加方便調試和控制。

一般在類內部推薦設置用setter 獲取直接用實例變量。

這里再說一下惰性加載,所謂惰性加載就是指,屬性會在第一次調用getter的時候初始化,如下:

-(NSString *)name
{
    if (!_name){
        _name = [[NSString alloc] init];
    }
    return _name;
}

那么此時就只能夠通過getter來調用實例變量了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM