舉例講解Objective-C中@property屬性的用法_IOS

來源:互聯網
上載者:User

學過c/c++的朋友都知道,我們定義struct/class的時候,如果把訪問限定符(public,protected,private)設定為public的話,那麼我們是可以直接用.號來訪問它內部的資料成員的。比如

//in Test.hclass Test{public:  int i;  float f;};

  我在main函數裡面是可以通過下面的方式來使用這個類的:(注意,如果在main函數裡面使用此類,除了要包含標頭檔以外,最重要的是記得把main.m改成main.mm,否則會報一些奇怪的錯誤。所以,任何時候我們使用c++,如果報奇怪的錯誤,那就要提醒自己是不是把相應的源檔案改成.mm尾碼了。其它引用此類的檔案有時候也要改成.mm檔案)

  //in main.mm  Test test;  test.i =1;  test.f =2.4f;    NSLog(@"Test.i = %d, Test.f = %f",test.i, test.f);

  但是,在objc裡面,我們能不能這樣做呢?請看下面的代碼:(建立一個objc類,命名為BaseClass)

複製代碼 代碼如下:

//in BaseClass.h
@interface BaseClass : NSObject{
@public
    NSString *_name;
}

 接下來,我們在main.mm裡面:
複製代碼 代碼如下:

    BaseClass *base= [[BaseClass alloc] init];
    base.name =@"set base name";
    NSLog(@"base class's name = %@", base.name);
   

  不用等你編譯,xcode馬上提示錯誤,請看截圖:

請大家注意看出錯提示“Property 'nam' not found on object of type BaseClass*",意思是,BaseClass這類沒有一個名為name的屬性。即使我們在標頭檔中聲明了@public,我們仍然無法在使用BaseClass的時候用.號來直接存取其資料成員。而@public,@protected和@private只會影響繼承它的類的存取權限,如果你使用@private聲明資料成員,那麼在子類中是無法直接使用父類的私人成員的,這和c++,java是一樣的。

  既然有錯誤,那麼我們就來想法解決啦,編譯器說沒有@property,那好,我們就定義property,請看代碼:

複製代碼 代碼如下:

//in BaseClass.h
@interface BaseClass : NSObject{
@public
    NSString *_name;
}
@property(nonatomic,copy) NSString *name;
//in BaseClass.m
@synthesize name = _name;

  現在,編譯並運行,ok,很好。那你可能會問了@prperty是不是就是讓”."號合法了呀?只要定義了@property就可以使用.號來訪問類的資料成員了?先讓我們來看下面的例子:
複製代碼 代碼如下:

@interface BaseClass : NSObject{
@public
    NSString *_name;
}
//@property(nonatomic,copy) NSString *name;

-(NSString*) name;
-(void) setName:(NSString*)newName;


  我把@property的定義注釋掉了,另外定義了兩個函數,name和setName,下面請看實現檔案:
複製代碼 代碼如下:

  現在,你再編譯運行,一樣工作的很好。why?因為我剛剛做的工作和先前聲明@property所做的工作完全一樣。@prperty只不過是給編譯器看的一種指令,它可以編譯之後為你產生相應的getter和setter方法。而且,注意看到面property(nonatomic,copy)括弧裡面這copy參數了嗎?它所做的事就是
複製代碼 代碼如下:

_name = [name copy];
  如果你指定retain,或者assign,那麼相應的代碼分別是:

//property(retain)NSString* name;
_name = [name retain];

//property(assign)NSString* name;
_name = name;


  其它講到這裡,大家也可以看出來,@property並不只是可以產生getter和setter方法,它還可以做記憶體管理。不過這裡我暫不討論。現在,@property大概做了件什麼事,想必大家已經知道了。但是,我們程式員都有一個坎,就是自己沒有完全吃透的東西,心裡用起來不踏實,特別是我自己。所以,接下來,我們要詳細深挖@property的每一個細節。

  首先,我們看atomic 與nonatomic的區別與用法,講之前,我們先看下面這段代碼:

複製代碼 代碼如下:

@property(nonatomic, retain) UITextField *userName;    //1

@property(nonatomic, retain,readwrite) UITextField *userName;  //2

@property(atomic, retain) UITextField *userName;  //3

@property(retain) UITextField *userName;  //4

@property(atomic,assign) int i;         // 5

@property(atomic) int i;         //6
@property int i;               //7


  請讀者先停下來想一想,它們有什麼區別呢?

  上面的代碼1和2是等價的,3和4是等價的,5,6,7是等價的。也就是說atomic是預設行為,assign是預設行為,readwrite是預設行為。但是,如果你寫上@property(nontomic)NSString *name;那麼將會報一個警告,如下圖:

 因為是非gc的對象,所以預設的assign修飾符是不行的。那麼什麼時候用assign、什麼時候用retain和copy呢?推薦做法是NSString用copy,delegate用assign(且一定要用assign,不要問為什麼,只管去用就是了,以後你會明白的),非objc資料類型,比如int,float等基礎資料型別 (Elementary Data Type)用assign(預設就是assign),而其它objc類型,比如NSArray,NSDate用retain。

  在繼續之前,我還想補充幾個問題,就是如果我們自己定義某些變數的setter方法,但是想讓編譯器為我們產生getter方法,這樣子可以嗎?答案是當然可以。如果你自己在.m檔案裡面實現了setter/getter方法的話,那以翻譯器就不會為你再產生相應的getter/setter了。請看下面代碼:

複製代碼 代碼如下:

//代碼一:
@interface BaseClass : NSObject{
@public
    NSString *_name;
}
@property(nonatomic,copy,readonly) NSString *name;  //這裡使用的是readonly,所有會聲明geter方法

-(void) setName:(NSString*)newName;

//代碼二:
@interface BaseClass : NSObject{
@public
    NSString *_name;
}
@property(nonatomic,copy,readonly) NSString *name;   //這裡雖然聲明了readonly,但是不會產生getter方法,因為你下面自己定義了getter方法。

-(NSString*) name;   //getter方法是不是只能是name呢?不一定,你開啟Foundation.framework,找到UIView.h,看看裡面的property就明白了)
-(void) setName:(NSString*)newName;

//代碼三:
@interface BaseClass : NSObject{
@public
    NSString *_name;
}
@property(nonatomic,copy,readwrite) NSString *name;  //這裡編譯器會我們產生了getter和setter

//代碼四:
@interface BaseClass : NSObject{
@public
    NSString *_name;
}
@property(nonatomic,copy) NSString *name;  //因為readwrite是預設行為,所以同代碼三


  上面四段代碼是等價的,接下來,請看下面四段代碼:  

複製代碼 代碼如下:

//代碼一:
@synthesize name = _name;  //這句話,編譯器發現你沒有定義任何getter和setter,所以會同時會你產生getter和setter

//代碼二:
@synthesize name = _name;  //因為你定義了name,也就是getter方法,所以編譯器只會為產生setter方法,也就是setName方法。

-(NSString*) name{
    NSLog(@"name");
    return _name;
}

//代碼三:
@synthesize name = _name;   //這裡因為你定義了setter方法,所以編譯器只會為你產生getter方法

-(void) setName:(NSString *)name{
    NSLog(@"setName");
    if (_name != name) {
        [_name release];
        _name = [name copy];
    }
}

//代碼四:
@synthesize name = _name;  //這裡你自己定義了getter和setter,這句話沒用了,你可以注釋掉。

-(NSString*) name{
    NSLog(@"name");
    return _name;
}

-(void) setName:(NSString *)name{
    NSLog(@"setName");
    if (_name != name) {
        [_name release];
        _name = [name copy];
    }
}


  上面這四段代碼也是等價的。看到這裡,大家對Property的作用相信會有更加進一步的理解了吧。但是,你必須小心,你如果使用了Property,而且你自己又重寫了setter/getter的話,你需要清楚的明白,你究竟幹了些什麼事。別寫出下面的代碼,雖然是合法的,但是會誤導別人:
複製代碼 代碼如下:

//BaseClass.h
@interface BaseClass : NSObject{
@public
    NSArray *_names;
}
@property(nonatomic,assgin,readonly) NSArray *names;  //注意這裡是assign

-(void) setNames:(NSArray*)names;

//BaseClass.m
@implementation BaseClass

@synthesize names = _names;

-(NSArray*) names{
    NSLog(@"names");
    return _names;
}

-(void) setNames:(NSArray*)names{
    NSLog(@"setNames");
    if (_name != name) {
        [_name release];
        _name = [name retain];  //你retain,但是你不覆蓋這個方法,那麼編譯器會產生setNames方法,裡面肯定是用的assign
    }
}


  當別人使用@property來做記憶體管理的時候就會有問題了。總結一下,如果你自己實現了getter和setter的話,atomic/nonatomic/retain/assign/copy這些只是給編譯的建議,編譯會首先會到你的代碼裡面去找,如果你定義了相應的getter和setter的話,那麼好,用你的。如果沒有,編譯器就會根據atomic/nonatomic/retain/assign/copy這其中你指定的某幾個規則去產生相應的getter和setter。

我們來整理一下@property中的屬性關鍵字:
1.原子性 nonatomic/atomic

在預設的情況下,由編譯器合成的方法會通過鎖定機制確保其原子性(atomicity)。如果具備nonatomic特質,則不使用同步鎖。

2.讀/寫入權限  readwrite/readonly

3.記憶體管理語義

assign “設定方法” 只會針對“純量類型”(scalar type, CGFloat或NSInteger等)的簡單賦值操作

strong “擁有關係” 為這種屬性設定新值時,設定方法先保留新值,並釋放舊值,然後再將新值設定上去

weak “非擁有關係” 為這種屬性設定新值時,設定方法既不保留新值,也不釋放舊值。此特質同assign類似,然而屬性所指的對象遭到摧毀時,屬性也會被清空(nil out)

unsafe_unretained 此特質的語義和assign相同,但是它適用於“物件類型”(object type),該特質表達一種“非擁有關係”(“不保留”,unretained),當目標對象遭到摧毀時,屬性值不會自動清空(“不安全”,unsafe),這一點與weak有區別

copy 此特質所表達的所屬關係與strong類似。然而設定方法並不保留新值,而是將其“拷貝”(copy)

4.方法名

getter=<name>

@property (nonatomic, getter=isOn) BOOL on;

setter=<name> 不太常用


總結
  好了,說了這麼多,回到我們的正題吧。atomic和nonatomic的作用與區別:

  如果你用@synthesize去讓編譯器產生代碼,那麼atomic和nonatomic產生的程式碼是不一樣的。如果使用atomic,如其名,它會保證每次getter和setter的操作都會正確的執行完畢,而不用擔心其它線程在你get的時候set,可以說保證了某種程度上的安全執行緒。但是,我上網查了資料,僅僅靠atomic來保證安全執行緒是很天真的。要寫出安全執行緒的代碼,還需要有同步和互斥機制。

  而nonatomic就沒有類似的“安全執行緒”(我這裡加引號是指某種程度的安全執行緒)保證了。因此,很明顯,nonatomic比atomic速度要快。這也是為什麼,我們基本上所有用property的地方,都用的是nonatomic了。

  還有一點,可能有讀者經常看到,在我的教程的dealloc函數裡面有這樣的代碼:self.xxx = nil;看到這裡,現在你們明白這樣寫有什麼用了吧?它等價於[xxx release];  xxx = [nil retain];(---如果你的property(nonatomic,retian)xxx,那麼就會這樣,如果不是,就對號入座吧)。

  因為nil可以給它發送任何訊息,而不會出錯。為什麼release掉了還要賦值為nil呢?大家用c的時候,都有這樣的編碼習慣吧。

  int* arr = new int[10];    然後不用的時候,delete arr; arr = NULL;  在objc裡面可以用一句話self.arr = nil;搞定。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.