iOS成員變量、實例變量、成員屬性、@property、@synthesize和 @dynamic 說明

一、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ī)情況下默認省略了。

  1. 如果這個成員變量(同名_ivar)已經(jīng)存在,@property就不再生成新成員變量;
  2. 默認合成的成員變量創(chuàng)建后默認是@private類型,只能在本類中訪問,子類也無法訪問父類默認生成的成員變量_ivar;
  3. 在.h聲明的成員變量會被子類訪問,是@protected類型。(補充:.h中聲明的成員變量都是protected,想要被非子類訪問需要用@public修飾)

接下來先看看以下問題:
1、那@synthesize,@dynamic到底是干什么的?
2、什么情況下不會自動合成成員變量和set/get方法呢?

2、@synthesize

@synthesize 是配合@property使用的。字面意思是合成,這個關(guān)鍵字在默認情況下可以省略,編譯器自動會實現(xiàn)這個關(guān)鍵字的功能,也可以手動加上實現(xiàn)。

  1. 如果屬性沒有手動實現(xiàn)setter和getter方法,編譯器為你自動生成setter與getter方法;
  2. 可以指定與屬性對應的實例變量(例如:@syntheszie ivar = _ivar123,就會為成員屬性ivar生成一個_ivar123的成員變量);
  3. 如果子類中有和父類重名的屬性,就會報錯:
    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)用對象的settergetter方法的一種快捷方式,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的目的。

參考:
iOS徹底搞清屬性與成員變量
objective-c指針解引用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容