【Objective-C】self.test と _testの表記の違いについて解説 【Xcode12.2対応】

こういう人に向けて発信しています。
・Ojective-Cの書き方に興味がある人
・現在勉強中でselfとアンダースコア表現が気になった人
・Objective-C慣れてきたけど疑問を感じて戻ってきた人

結論:明確な違いはある。

(1)selfを用いる際はオーバーライドしたsetterメソッドを経由してから
セットされる
(2)アンダースコアで参照する場合は、プロパティ属性を無視してしまう。

ただ、値を変更した際にselfだと特定のメソッドを呼び出せるのです。
(必ず通ってから値がセットされる)

その1:selfを用いる際はオーバーライドしたsetterメソッドを経由してからセットされる

@interface ViewController ()
@property (nonatomic) NSString *testString;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _testString = @"text01";   //(1)
    self.testString = @"text02";   //(2)
    
}

-(void)setTestString:(NSString *)testString{
    if([testString isEqualToString:@"text02"]){
        _testString = testString;
    }
}
@end

上記の場合、(1)が処理された段階ではsetTestStringが呼ばれませんが、
(2)が処理された場合は通ります。

-(void)setTestString:(NSString *)testString{ 何?

これがいわゆるセッターというアクセサメソッドです。
ドット演算子(self.)を使用して値を変更される際には、
こちらが呼び出されています。(裏では)

-(void)setTestString:(NSString *)testString{
        _testString = testString;
}

明記しなくてもこんな感じの処理が呼ばれています。(ゲッターも同様)

selfで値を変更する事で出来る事

下記のコードのように、セットされた値を判定式を噛ませる事が出来ます。

//_testStringという変数には@"test"以外受け付けないという式になる。

-(void)setTestString:(NSString *)testString{    <- A部分
    if(testString = @"test"){  
        _testString = testString;    <- C部分
    }
}

補足だが、代入する一行で self.testStringを入れてはいけない理由は、
「無限ルート」を起こさないためです。

なぜsetter内でselfを用いると無限ループになるのか。

self.(ドット演算子)でアクセスして、setterがオーバーライズされていると
呼ばれるのであれば、この中でselfを使ったら、
またselfのsetterが呼ばれてしまうからです。
上記コードのC部分でselfを使うと強制的にA部分が呼び出されます。

その2:アンダースコアで参照する場合は、プロパティ属性を無視してしまう。

@interface ViewController (){
    //インスタンス変数
    NSString *_test; ///(1)
}

@property (nonatomic,readonly) NSString *test; ///(2)
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _test = @"test";  //(1)を参照
    self.test = @"test";  //(2)を参照  // error:Assignment to readonly property;
}

インスタンス変数とプロパティ宣言を用いてみました。
(1)インスタンス変数
(2)プロパティ宣言

インスタンス変数について

何もプロパティ宣言をしない場合は、
プロパティは strong,readwriteで宣言されている扱いになります。
(これ合っているはずだと思います)

なので、もちろん編集可能となっております。

プロパティ宣言

@property (nonatomic,readonly) NSString *test;

上記プロパティ宣言では、
nonatomic,readonlyと読み書き不能とプロパティ属性を宣言しております。

なので値の更新ができないと。

何が問題なのか。

@propertyでプロパティ属性を考えて、
指定して宣言していたにも関わらず、
無視して上書きしてしまいました。

これは意図しないコーディング方針になりますよね。
そういった事故が起こりかねないというわけです。

補足:インスタンス変数宣言の部分が無くても同じ結果になる。

なぜならプロパティ宣言した時点で、
自動的に _testというインスタンス変数が裏で宣言されているからです。

結論:

そのクラス内のコーディングの方針や目的に従うのであれば、
selfを使うのが好ましいが、
setterなどを利用している場合で避ける場合や
setter内などに関してはアンダースコアで参照するべきである。



いいなと思ったら応援しよう!