1. 可以用@property語法來定義對象中所封裝的數據。 2. 通過“修飾詞”來指定存儲數據所需的正確語義。 3. 在設置屬性所對應的實例變數時,一定要遵從該屬性所聲明的語義。 4. 開發iOS程式時應該使用nonatomic屬性,因為atomic(同步鎖)屬性嚴重影響性能。 ...
屬性
“屬性”(property)是OC的一項特性,用於封裝對象中的數據。
@property
@Property是聲明屬性的語法(
@property = ivar + getter + setter
)。OC對象通常會把其所需的數據保存為各種實例變數(
ivar
)。實例變數一般通過“存取方法”(accessmethod
)來訪問。什麼是存取方法:
getter
和setter
方法(access method = getter + setter
),其中getter
用於獲取變數value
, 而setter
用於寫入value
。@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
)。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修飾的屬性並不會使引用計數增加。
- 如果使用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
後將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
(同步鎖)屬性嚴重影響性能。