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