「屬性」(property)是OC的一項特性,用於封裝對象中的數據。安全
@property
@Property是聲明屬性的語法(@property = ivar + getter + setter
)。多線程
OC對象一般會把其所需的數據保存爲各類實例變量(ivar
)。實例變量通常經過「存取方法」(accessmethod
)來訪問。性能
什麼是存取方法:
getter
和setter
方法(access method = getter + setter
),其中getter
用於獲取變量value
, 而setter
用於寫入value
。atom
@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;
點語法有什麼優點呢?對象
省時,省力 :若是使用了屬性,編譯器會自動編寫訪問屬性所需的方法。這個過程由編譯器在編譯期執行,看不到這些
get set
源代碼。內存編譯器會自動向類中添加適當類型的實例變量,而且在屬性名前添加下劃線。開發
若是你不想讓編譯器自動合成存取方法,則能夠本身實現。若是你只實現了其中一個存取方法,那麼另外一個仍是會由編譯器來合成。
當咱們同時重寫了setter and getter
方式時,系統會報錯,緣由是找不到實例變量。其解決方法: 在.m
的文件中使用@synthesize
。
@synthesize
@synthesize
是爲屬性添加一個實例變量名,或者說別名。同時會爲該屬性生成 setter/getter
方法。
在protocol
中使用property
只會生成setter
和getter
方法聲明,咱們使用屬性的目的,是但願遵照我協議的對象能實現該屬性。須要使用@synthesize
生成setter
和getter
。
當你在子類中重載了父類中的屬性,你必須 使用@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
中定義與變量名不一樣的setter
和getter
的命名,以此來保護變量不會被不恰當的訪問(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
告訴編譯器:屬性的 setter
與 getter
方法由用戶本身實現,不自動生成。(固然對於 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
意爲操做是原子的,意味着只有一個線程訪問實例變量(生成的setter
和getter
方法是一個原子操做)。atomic
是線程安全的,至少在當前的存取器上是安全的。它是一個默認的特性,可是不多使用,由於比較影響效率。
nonatomic
:nonatomic
意爲操做是非原子的,能夠被多個線程訪問。它的效率比atomic
快。但不能保證在多線程環境下的安全性,開發中經常使用。
開發iOS程序時應該使用nonatomic
屬性,由於atomic
(同步鎖)屬性嚴重影響性能。該屬性使用了同步鎖,會在建立時生成一些額外的代碼用於幫助編寫多線程程序,這會帶來性能問題,經過聲明nonatomic
能夠節省這些雖然很小可是沒必要要額外開銷。
readwrite
(默認):readwrite
是默認值,表示該屬性同時擁有setter
和getter
。
readonly
: readonly
表示只有getter
沒有setter
。
有時候爲了語意更明確可能須要自定義訪問器的名字。
//setter=<name>這種不經常使用,也不推薦使用 @property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name; @property (nonatomic,getter = isHidden ) BOOL hidden;
assign
(默認):assign
用於非指針變量(值)類型,統一由系統棧進行內存管理。通常用於基礎類型和C
數據類型,如int
、float
、double
和NSInteger
,CGFloat
等表示單純的複製。還包括不存在全部權關係的對象,好比常見的delegate
。
retain
:在setter
方法中,須要對傳入的對象進行引用計數加1
的操做。
strong
:strong
是在IOS
引入ARC
的時候引入的關鍵字,是retain
的一個可選的替代。對傳入的對象的強引用,會增長對象的引用計數。strong
跟retain
的意思相同併產生相同的代碼,可是語意上更好更能體現對象的關係。
weak
:對傳入的對象的弱引用,不增長對象的引用計數,也不持有對象,當對象消失後指針自動指向nil
。
copy
:與strong
相似,但區別在於copy
是建立一個新對象,strong
是建立一個指針,引用對象計數加1
。
weak
與strong
與copy
屬性特質的差別Person
類的實例變量,並分別用weak
與strong
修飾。@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修飾的屬性並不會使引用計數增長。
@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
後將p
置nil
。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=xiaoming
與after p.name=xiaoming
。由於copy
關鍵字修飾的屬性是將對象拷貝一份賦值,因此你改變原對象並不會對拷貝後的對象有任何改變。
注:用@property
聲明 NSString
、NSArray
、NSDictionary
常用copy
關鍵字,是由於他們有對應的可變類型:NSMutableString
、NSMutableArray
、NSMutableDictionary
,他們之間可能進行賦值操做,爲確保對象中的字符串值不會無心間變更,應該在設置新屬性值時拷貝一份.
能夠用@property
語法來定義對象中所封裝的數據。
經過「修飾詞」來指定存儲數據所需的正確語義。
在設置屬性所對應的實例變量時,必定要聽從該屬性所聲明的語義。
開發iOS程序時應該使用nonatomic
屬性,由於atomic
(同步鎖)屬性嚴重影響性能。