【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内などに関してはアンダースコアで参照するべきである。