52個有效方法(6) - 理解「屬性」這一律念

屬性

「屬性」(property)是OC的一項特性,用於封裝對象中的數據。安全

@property

  • @Property是聲明屬性的語法(@property = ivar + getter + setter)多線程

    OC對象一般會把其所需的數據保存爲各類實例變量(ivar)。實例變量通常經過「存取方法」(accessmethod)來訪問。性能

    什麼是存取方法:gettersetter 方法(access method = getter + setter),其中getter 用於獲取變量value, 而setter 用於寫入valueatom

  • @Property能夠快速方便的爲實例變量建立存取器。線程

//  Man.h
#import <Foundation/Foundation.h>

@interface Man : NSObject
@property (nonatomic,strong)NSString *name;
@property (nonatomic,strong)NSString *sex;
@end

與下面的寫法等效指針

//  Man.h
#import <Foundation/Foundation.h>

@interface Man : NSObject
{
    // 實例變量
    NSString *name;
    NSString *sex;
}
// setter
- (void)setName:(NSString *)newName;

// getter
- (NSString *)name;

// setter
- (void)setSex:(NSString *)newSex;

// getter
- (NSString *)sex;
@end
  • 一般使用「點語法」 來讓編譯器自動調用相關的存取方法(access method = getter + setter)。code

    self. name = @"sky";
    NSString *name = self. name;

    點語法有什麼優點呢?對象

    1. 省時,省力 :若是使用了屬性,編譯器會自動編寫訪問屬性所需的方法。這個過程由編譯器在編譯期執行,看不到這些get set 源代碼。內存

    2. 編譯器會自動向類中添加適當類型的實例變量,而且在屬性名前添加下劃線。開發

  • 若是你不想讓編譯器自動合成存取方法,則能夠本身實現。若是你只實現了其中一個存取方法,那麼另外一個仍是會由編譯器來合成。

  • 當咱們同時重寫了setter and getter方式時,系統會報錯,緣由是找不到實例變量。其解決方法: 在.m的文件中使用@synthesize

@synthesize

  • @synthesize是爲屬性添加一個實例變量名,或者說別名。同時會爲該屬性生成 setter/getter 方法。

  • protocol中使用property只會生成settergetter方法聲明,咱們使用屬性的目的,是但願遵照我協議的對象能實現該屬性。須要使用@synthesize生成settergetter

  • 當你在子類中重載了父類中的屬性,你必須 使用@synthesize來手動合成ivar

  • 當咱們同時重寫了setter and getter方式時,須要在.m的文件中使用@synthesize

    //  Man.m
    #import "Man.h"
    
    @implementation Man
    @synthesize name = _name;
    // setter
    - (void)setName:(NSString *)name
    {
      _name = name;
    }
    
    // getter
    - (NSString *)name
    {
      return _name;
    }
    @end
    • **@synthesize name = _name**

      • _name是成員變量

      • name是屬性

      • 做用是告訴編譯器name屬性爲_name實例變量生成setter and getter方法的實現

      • name屬性的setter方法是setName,它操做的是_name這個變量

      • @synthesize中定義與變量名不一樣的settergetter的命名,以此來保護變量不會被不恰當的訪問setter=<name>這種不經常使用,也不推薦使用)

        //setter=<name>這種不經常使用,也不推薦使用
        @property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name;
        
        @property (nonatomic,getter = isHidden ) BOOL hidden;
  • @property有兩個對應的詞,一個是 @synthesize,一個是 @dynamic。若是 @synthesize@dynamic都沒寫,那麼默認的就是@syntheszie var = _var

  • 若是某屬性已經在某處實現了本身的 setter/getter ,可使用 @dynamic來阻止 @synthesize 自動生成新的 setter/getter 覆蓋。

@dynamic

  • @dynamic告訴編譯器:屬性的 settergetter 方法由用戶本身實現,不自動生成。(固然對於 readonly 的屬性只需提供 getter 便可)。

  • 假如一個屬性被聲明爲 @dynamic var,而後你沒有提供@setter方法和 @getter 方法。編譯的時候沒問題,可是當程序運行到 instance.var = someVar,因爲缺 setter 方法會致使程序崩潰。或者當運行到 someVar = var時,因爲缺 getter 方法一樣會致使崩潰。

  • 編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。

//  Man.h
#import <Foundation/Foundation.h>

@interface Man : NSObject
@property (nonatomic,strong)NSString *name;
@end
//  Man.m
#import "Man.h"

@implementation Man
@dynamic name;
// setter
// - (void)setName:(NSString *)name
// {
//   _name = name;
// }
// getter
- (NSString *)name
{
  return _name;
}
@end

調用時會出現崩潰

Man *man = [[Man alloc] init];
    man.name = @"sky";//缺 setter 方法會致使程序崩潰
    NSString *name = man.name;//缺 getter 方法一樣會致使崩潰

屬性特質

原子性

  • atomic(默認):atomic意爲操做是原子的,意味着只有一個線程訪問實例變量(生成的settergetter方法是一個原子操做)。atomic是線程安全的,至少在當前的存取器上是安全的。它是一個默認的特性,可是不多使用,由於比較影響效率。

  • nonatomicnonatomic意爲操做是非原子的,能夠被多個線程訪問。它的效率比atomic快。但不能保證在多線程環境下的安全性,開發中經常使用。

  • 開發iOS程序時應該使用nonatomic屬性,由於atomic(同步鎖)屬性嚴重影響性能。該屬性使用了同步鎖,會在建立時生成一些額外的代碼用於幫助編寫多線程程序,這會帶來性能問題,經過聲明nonatomic能夠節省這些雖然很小可是沒必要要額外開銷。

存取器控制

  • readwrite(默認):readwrite是默認值,表示該屬性同時擁有settergetter

  • readonlyreadonly表示只有getter沒有setter

  • 有時候爲了語意更明確可能須要自定義訪問器的名字。

//setter=<name>這種不經常使用,也不推薦使用
@property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name;
  

@property (nonatomic,getter = isHidden ) BOOL hidden;

內存管理

  • assign(默認):assign用於非指針變量(值)類型,統一由系統棧進行內存管理。通常用於基礎類型和C數據類型,如intfloatdoubleNSIntegerCGFloat等表示單純的複製。還包括不存在全部權關係的對象,好比常見的delegate

  • retain:在setter方法中,須要對傳入的對象進行引用計數加1的操做。

  • strongstrong是在IOS引入ARC的時候引入的關鍵字,是retain的一個可選的替代。對傳入的對象的強引用,會增長對象的引用計數。strongretain的意思相同併產生相同的代碼,可是語意上更好更能體現對象的關係。

  • weak:對傳入的對象的弱引用,不增長對象的引用計數,也不持有對象,當對象消失後指針自動指向nil

  • copy:與strong相似,但區別在於copy是建立一個新對象,strong是建立一個指針,引用對象計數加1

舉例說明weakstrongcopy屬性特質的差別

  • 首先建立兩個自定義的Person類的實例變量,並分別用weakstrong修飾。
@property (nonatomic,strong) Person *strongPerson;
@property (nonatomic,weak) Person *weakPerson;
  • strongPerson屬性置nil
self.strongPerson = [[Person alloc] init];
self.weakPerson = self.strongPerson;
self.strongPerson = nil; 
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);

輸出結果爲:strongStr=(null),weakStr=(null)。說明weak修飾的屬性並不會使引用計數增長。

  • 若是使用NSString類進行上述相似操做,獲得的結果是不一樣的。
@property (nonatomic,strong) NSString *strongStr;
@property (nonatomic,weak) NSString *weakStr;
···
self.strongStr = @"string";
self.weakStr = self.strongStr;
self.strongStr = nil;
NSLog(@"strongStr=%@,weakStr=%@",self.strongStr,self.weakStr);

輸出結果爲:strongStr=(null),weakStr=string。這裏主要是由於NSString類型的賦值默認會加上copy,而copy會建立一個新的對象。這裏的賦值語句實際上是

self.strongStr = [@"string" copy];
self.weakStr = [self.strongStr copy];
  • weakPerson屬性置nil
self.strongPerson = [[Person alloc] init];
self.weakPerson = self.strongPerson;
self.weakPerson = nil;
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);

輸出結果以下:strongStr=<Person: 0x600000007d50>,weakStr=(null)。說明weak修飾的屬性只是對對象的弱引用,並不會真正的持有該對象。

  • 新建一個Person類實例變量p,賦值strongPerson後將pnil
Person *p = [[Person alloc] init];
self.strongPerson = p;
self.weakPerson = self.strongPerson;
p = nil; 
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);

輸出結果爲:strongStr=<Person: 0x600000200b50>,weakStr=<Person: 0x600000200b50>。由於strong屬性會強引用該對象並使該對象的引用計數+1,因此即便把p設置爲nil,該對象也並無釋放,要想釋放該對象,還得把strongStr設置爲nil:self.strongPerson = nil;。這樣輸出結果才爲 strongStr=(null),weakStr=(null)

  • 在給Person類加了一個name屬性。並用copy修飾 :(@property (nonatomic,copy) NSString *name)。
NSString *a = @"xiaoming";
Person *p = [[Person alloc] init];
p.name = a;
NSLog(@"before p.name=%@",p.name);
a = @"xiaohua";
NSLog(@"after p.name=%@",p.name);

輸出結果:before p.name=xiaomingafter p.name=xiaoming。由於copy關鍵字修飾的屬性是將對象拷貝一份賦值,因此你改變原對象並不會對拷貝後的對象有任何改變。

注:用@property聲明 NSStringNSArrayNSDictionary 常用copy關鍵字,是由於他們有對應的可變類型:NSMutableStringNSMutableArrayNSMutableDictionary,他們之間可能進行賦值操做,爲確保對象中的字符串值不會無心間變更,應該在設置新屬性值時拷貝一份.

要點

  1. 能夠用@property語法來定義對象中所封裝的數據。

  2. 經過「修飾詞」來指定存儲數據所需的正確語義。

  3. 在設置屬性所對應的實例變量時,必定要聽從該屬性所聲明的語義。

  4. 開發iOS程序時應該使用nonatomic屬性,由於atomic(同步鎖)屬性嚴重影響性能。

相關文章
相關標籤/搜索