OC中self a和_a的訪問的區別

TongRy發表於2017-12-13

揚帆起航
久了不回顧基礎知識,猛然間看到,有時候還真想不起來有的知識,比如宣告@property和合成@synthesize的屬性與普通的屬性有什麼本質區別,self.a和_a訪問的區別,所以有必要回顧一下。


我們先來說說@property和@synthesize @property宣告成員變數,會自動幫我們生成該成員變數的getter/setter方法的宣告; @synthesize的作用是自動生成成員變數的getter/setter方法的定義; 所有被宣告為屬性的成員,在iOS5 之前需要使用編譯器指令@synthesize 來告訴編譯器幫助生成屬性的getter,setter方法。之後這個指令可以不用人為指定了,預設情況下編譯器會幫我們生成。 這裡先淺顯的說明@property和synthesize的作用,其他的知識自己google,今天主要回顧self.a和_a的區別。


首先使用self.a會呼叫getter/setter方法,而_a並不會呼叫getter/setter方法,它是直接訪問例項變數並賦值。 下面我們通過@property的copy屬性舉例說明:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;

- (void)changeNameValue:(NSString *)newName andChangeSexValue:(NSString *)sexValue;

@end
複製程式碼

上面是一個Person類,裡面有兩個成員變數name和sex,宣告瞭一個例項方法changeNameValue:andChangeSexValue:,下面例項方法的實現:

#import "Person.h"

@implementation Person

- (void)changeNameValue:(NSString *)newName andChangeSexValue:(NSString *)sexValue
{
    self.name = newName;
    _sex = sexValue;
}

@end
複製程式碼

然後在viewDidLoad方法中呼叫:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSMutableString *newName =[NSMutableString stringWithString:@"TongRy"];
    NSMutableString *newSex = [NSMutableString stringWithString:@"man"];
    
    Person *xiaoming = [[Person alloc] init];
    [xiaoming changeNameValue:newName andChangeSexValue:newSex];
    
    NSLog(@"xiaoming newName: %@, newSex: %@;", xiaoming.name, xiaoming.sex);
    
    [newName appendString:@"Good"];
    [newSex appendString:@"andwoman"];
    NSLog(@"To observe the changes : xiaoming name: %@, sex: %@;", xiaoming.name, xiaoming.sex);
    
}
複製程式碼

執行後得到的結果:

執行結果
我們可以看到,newName和newSex改變了,name的值仍然沒有變是TongRy,而sex的值確是改變了,末尾增加了“andwoman”.

實際上我們期待的是對類屬性的賦值是深拷貝賦值(我們宣告瞭@property的copy屬性),但是實際得到的結果是name進行了深拷貝,而sex仍然是淺拷貝。究其原因,就是因為name是self訪問,sex是_訪問。在呼叫self的時候,如果是賦值,那麼編譯器會自動根據strong/copy生成對應的setter方法,其實現類似於:

//宣告為copy屬性時
- (void)setName:(NSString *)name
{
    if (_name != name)
    {
        [_name release];
        _name = [name copy];
    }
}

//當宣告為retain屬性時(MRC下)
- (void)setName:(NSString *)name
{
    if (_name != name)
    {
        [_name release];
        [name retain];
        _name = name;
    }
}
複製程式碼

在上面的例子中,使用self.name賦值後,name已經和newName沒有指向同一塊記憶體,所以name沒有隨著newName值的改變而改變。_sex賦值是直接指向了newSex所指向的記憶體塊,也沒有做retain操作,容易出現問題。所以,我們在類中應該儘量使用self.a的形式來訪問屬性。

用self.name 是更好的選擇,因為這樣可以相容懶載入,同時也避免了使用下劃線的時候忽視了self這個指標,後者容易在block中造成迴圈引用。同時,使用_是獲取不到父類的屬性,因為它只是對區域性變數的訪問。

相關文章