一、iOS成員變量、實例變量、成員屬性說明:
1、成員變量、實例變量:
1)、成員變量是在{}中聲明的變量,如下代碼所示:
2)、如果成員變量的類型是一個類則稱這個變量為實例變量
3)、成員變量包括實例變量,所以可以通稱為成員變量(這里只是便于概念理解分開解釋)
實例變量 = 成員變量 = ivar
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Persion : NSObject{
NSString *name; //實例變量
int age; //成員變量
}
@end
NS_ASSUME_NONNULL_END
2、成員屬性(也可稱屬性變量):
通常我們使用@property聲明的變量都叫做成員屬性,也可稱屬性變量。
3、成員變量和成員屬性的關(guān)系:
1、屬性對成員變量擴充了存取方法 (例如在get和set方法中做其他邏輯);
2、屬性默認會生成帶下劃線的成員變量 ;
3、但只聲明了變量,是不會有屬性的,可以通過以下代碼證明:
在Person.h 頭文件中
@interface Person : NSObject {
@private
//name為私有成員變量
NSString *name;
}
// age 為成員屬性
@property (nonatomic ,copy) NSString *age;
在viewController.m 中,通過RunTime機制獲得對象的所有成員變量和成員屬性。
Person *p = [Person new];
unsigned int count = 0; //count記錄變量的數(shù)量
// 獲取類的所有成員變量
Ivar *members = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = members[i];
// 取得變量名并轉(zhuǎn)成字符串類型
const char *memberName = ivar_getName(ivar);
NSLog(@"變量名 = %s",memberName);
}
// 獲取類的所有成員屬性
objc_property_t *properties =class_copyPropertyList([Person class], &count);
for (int i = 0; i<count; i++)
{
objc_property_t property = properties[i];
const char* char_f =property_getName(property);
NSString *propertyName = [NSString stringWithUTF8String:char_f];
NSLog(@"屬性名 = %@",propertyName);
}
打印結(jié)果為
變量名 = name
變量名 = _age
屬性名 = age
二、@property、@synthesize、@dynamic說明:
1、@property
@property是用來定義成員屬性的,通常情況會自動合成成員變量和set/get方法。
簡單來說:我們寫@property聲明屬性,其實是做了三件事(@property = _ivar + getter + setter):
.h: 聲明了getter和setter方法;
.m: 聲明了成員變量(默認:下劃線+屬性名);
.m: 實現(xiàn)了getter和setter方法。
以上三件事是由編譯器自動加上了@synthesize關(guān)鍵字的功能,只是常規(guī)情況下默認省略了。
- 如果這個成員變量(同名_ivar)已經(jīng)存在,@property就不再生成新成員變量;
- 默認合成的成員變量創(chuàng)建后默認是@private類型,只能在本類中訪問,子類也無法訪問父類默認生成的成員變量_ivar;
- 在.h聲明的成員變量會被子類訪問,是@protected類型。(補充:.h中聲明的成員變量都是protected,想要被非子類訪問需要用@public修飾)
接下來先看看以下問題:
1、那@synthesize,@dynamic到底是干什么的?
2、什么情況下不會自動合成成員變量和set/get方法呢?
2、@synthesize
@synthesize 是配合@property使用的。字面意思是合成,這個關(guān)鍵字在默認情況下可以省略,編譯器自動會實現(xiàn)這個關(guān)鍵字的功能,也可以手動加上實現(xiàn)。
- 如果屬性沒有手動實現(xiàn)setter和getter方法,編譯器為你自動生成setter與getter方法;
- 可以指定與屬性對應的實例變量(例如:@syntheszie ivar = _ivar123,就會為成員屬性ivar生成一個_ivar123的成員變量);
- 如果子類中有和父類重名的屬性,就會報錯:
Auto property synthesis will not synthesize property 'name';
it will be implemented by its superclass, use @dynamic to acknowledge intention
這是因為當編譯器檢測到父類相同屬性的時候子類不會自動生成@sythesize ivar = _ivar,此時子類只有屬性沒有生成對應成員變量_ivar,也不會有對應的set和get方法。
在子類調(diào)用self.ivar時實際上是調(diào)用父類的屬性。一旦這個子類的屬性是開發(fā)者自定義的,開發(fā)者用這個屬性調(diào)用方法時用了自定義的方法,這個方法父類屬性沒有的時候,就會造成崩潰;
這個時候可以在子類添加@syntheszie ivar = _ivar ,子類會生成自己私有的_ivar成員變量,這個時候子類的self.ivar也就會訪問自己的屬性。
另外,以下這些場景定義的屬性不會合成成員變量:
1)同時重寫了 setter 和 getter 時
2)重寫了只讀屬性的 getter 時,如下第三部分readonly 和 writeonly情況下重寫
3)使用了 @dynamic 時
4)在 @protocol 中定義的所有屬性
5)在 category 中定義的所有屬性
6)重載的屬性(如果子類中有和父類重名的屬性,就會警告,需要用@synthesize)
如果條件滿足且需要成員變量可以使用@synthesize關(guān)鍵字來合成
3、@dynamic
@dynamic告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現(xiàn),不自動生成。
假如一個屬性被聲明為 @dynamic var,而且你沒有提供 @setter方法和 @getter 方法,編譯的時候沒問題,但是當程序運行到 instance.var = someVar,由于缺 setter 方法會導致程序崩潰;或者當運行到 someVar = var 時,由于缺 getter 方法同樣會導致崩潰。編譯時沒問題,運行時才執(zhí)行相應的方法,這就是所謂的動態(tài)綁定。
三、重寫getter和setter方法注意事項
只重寫getter(懶加載):默認會自動生成下劃線開頭的變量,在getter中要使用下劃線(return _ivar)來返回值,不能使用self.否則造成死循環(huán)
只重寫setter:默認會自動生成下劃線開頭的變量,在setter中要使用下劃線( _ivar= ivar)來接收值,不能使用self.否則造成死循環(huán)
兩個都重寫:同時手動重寫了一個屬性的get和set方法的話,Xcode不會再自動生成帶有下劃線的私有成員變量了這時如果不加,@synthesize就會報錯,解決方法就是添加@syntheszie ivar = _ivar
readonly 和 writeonly情況下重寫:這時屬性只會生成getter或者setter方法,如果我們重寫了該方法,就需要我們重新添加@synthesize
四、Objective-C 中的點語法
-
點表達式(.)看起來與C語言中的結(jié)構(gòu)體訪問以及java語言匯總的對象訪問有點類似,如果點表達式出現(xiàn)在等號=左邊,調(diào)用該屬性名稱的setter方法。如果點表達式出現(xiàn)在=右邊,調(diào)用該屬性名稱的getter方法。 - OC中
點表達式(.)其實就是調(diào)用對象的setter和getter方法的一種快捷方式,self.myString = @"張三";實際就是[self setmyString:@"張三"];
屬性訪問方式 :
這是我們最容易掌握的一種使用方式,所以甚至有的開發(fā)者在開發(fā)中只會定義屬性
person .name = @"xiaoming";
指針訪問方式 :
作為一個有潔癖的程序員,更多時候還是定義成員變量而不是屬性,因為至少減少了一次方法調(diào)用,減少了內(nèi)存占用
person->_name = @"xiaowang";
KVC訪問方式 :
如果一個類的成員變量是私有的,然后我想訪問它,可以使用KVC的方式
[person setValue:@"xiaohua" forKey:@"name"];
五、self.ivar和_ivar的區(qū)別
其中self.ivar是調(diào)用的xx屬性的get/set方法,而_ivar則只是使用成員變量_ivar,并不會調(diào)用get/set方法。兩者的更深層次的區(qū)別在于,通過存取方法訪問比直接訪問多做了一些其他的事情(例如內(nèi)存管理,復制值等),例如如果屬性在@property中屬性的修飾符有retain,那么當使用self.xx的時候相應的屬性的引用計數(shù)器由于生成了setter方法而進行加1操作,此時的retaincount為2
六、屬性、成員變量、self.ivar、_ivar使用經(jīng)驗總結(jié)
需要與外部類交互的都寫成屬性
所有屬性在使用時最好使用self.來調(diào)用,其他內(nèi)部使用的對象盡量用成員變量定義(減少內(nèi)存占用,調(diào)用更快)
需要懶加載的對象定義為屬性(或私有屬性)
重寫getter(懶加載)和setter方法時在內(nèi)部使用_ivar來操作,避免造成死鎖。
七、其他
1、經(jīng)??吹絙lock里面有報警:
Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior
block中使用了self的成員變量_ivar,因此block會隱式的retain住self。Xcode認為這可能會給開發(fā)者造成困惑,或者因此而因襲循環(huán)引用,所以警告我們要顯示的在block中使用self,以達到block顯示retain住self的目的。