なぜinitとdealloc関数でaccessorメソッドを使用できないのか

5539 ワード

前言
なぜinitとdeallocメソッドでgetterとsetterを呼び出さないのか:AppleはMacとiOSのメモリ管理に関する開発ドキュメントで、「Don'tUser Accessor Methods in Initializer Methods and dealloc」というセクションがあります.「Theonly places you shouldn’t use accessor methods to set an instancevariable are in initializer methods anddealoc.」しかし、なぜかは説明されていない.ネット上でいくつかの国内国外の文章といくつかの大きいVのブログを検索して、この文章がみんなの疑惑を詳しく尽くすことができることを望んで、尽きないところは伝言を残して正してください.
なぜinitでaccessorを呼び出せないのか
ケース1
次のコードでは、既存の2つのクラスBaseClassとSubClassがあり、SubClassはBaseClassから継承されています.親にはvalue属性があります(サブクラスにも自然に統合されます).親のinit(または他の初期化構造方法)でvalueのsetterが使用されている場合、サブクラスもvalueのsetterを書き換えている場合、問題が発生します.理由は次のとおりです.子クラスがinit(または他の初期化構造方法)を呼び出してオブジェクトを初期化する場合、子クラスのinitはまず親のinitを呼び出します.(self=[super init])は、親のinitメソッドに移動し、親のinitメソッドでsetterを呼び出してvalue属性に値を割り当てます.親は、子の書き換えのsetter(子がvalueのsetterを書き換えたため)を直接呼び出します.この場合、サブクラスオブジェクトはまだ初期化されていませんが、サブクラスvalueのsetterがサブクラス独自のinitコード呼び出しより先に(サブクラスのinitメソッドにreturn selfがないため)、問題が発生する可能性があります.インスタンス変数の値を変更するなど、サブクラスのsetterメソッドで他の操作を行った場合、selfが初期化されていないためエラーが発生します.この問題の原因は2つあります.1つは、親のinitでsetterを使用していることです.2つ目は、サブクラスがsetterを書き換えると、親クラスinit時にサブクラス書き換えのsetterが呼び出されるため、書き換えたsetterでサブクラス特有の操作が行われた場合、サブクラスの属性付与に失敗するなど、サブクラスオブジェクトselfの初期化が完了していないため、問題が発生する可能性があります.
ケース2
親のinitメソッドでvalueのsetterが使用され、親にsetterが書かれている場合.サブクラスが初期化されると、親クラスのinitメソッド、すなわちself=[super init]が呼び出されます.親クラスでvalueのsetterが使用されているため、親クラスのinitはvalueのsetterに移動します.setterでネットワークリクエストを送信するなど、他の操作が行われている場合、問題が発生する可能性があります.サブクラスオブジェクトがsetterを介してvalueに値を割り当てると、親クラスのsetterが呼び出されます.親クラスに相当するsetterが2回呼び出され、同じネットワークリクエストが2回送信されます.
init call accessor Example:
@interface BaseClass : NSObject
@property(nonatomic) NSString* info;
@end

@implementation BaseClass
- (instancetype)init {  
    if ([super init]) {
        self.info = @"baseInfo"; 
    } 
    return self;
}
@end
@interface SubClass : BaseClass
@end
@interface SubClass ()
@property (nonatomic) NSString* subInfo;
@end

@implementation SubClass
- (instancetype)init {
     if (self = [super init]) {
         self.subInfo = @"subInfo"; 
    } 
    return self;
}

- (void)setInfo:(NSString *)info {
    [super setInfo:info]; 
    NSString* copyString = [NSString stringWithString:self.subInfo]; NSLog(@"%@",copyString);
}
@end

[[SubClass alloc]init]を実行すると、Initメソッドで親クラスが呼び出されます.ここでaccessorが呼び出され、親セクションのinfoプロパティが初期化されます.正常に見えますが、サブクラスがメソッドを書き換えると、マルチステートが呼び出されるのはサブクラスのaccessorメソッドです.サブクラスのaccessor実装におけるコードは、サブクラス部分が初期化されていることを前提として記述されている.すなわち、サブクラス部分は初期化されており、完全に利用可能であるが、現実的にはinitメソッドが実行されていないため、この仮定は成立せず、クラッシュを引き起こす可能性がある.以上の例には人工的な痕跡があり,現実にはある方法が1回少なく呼び出され,論理エラーが発生することが多い.
なぜdeallocでaccessorを呼び出せないのですか?
それともサブクラスが親クラスのvalue属性を書き換えたという前提に基づいて、サブクラスオブジェクトが破棄されると、まずサブクラスのdeallocが呼び出され、最後に、親のdeallocが呼び出されます(これはinit初期化方法とは逆であり、ARCでは[super dealloc]を手動で呼び出す必要はありません).親がdeallocでvalueのaccessorを呼び出し、そのaccessorがクラスに書き換えられた場合、子のaccessorに呼び出されます.ただし、この場合、子のクラスは解放されます(子のdeallocを先に呼び出し、後に親のdeallocを呼び出すため).エラーが発生したり崩壊したりしますdealloc call accessor example

@interface BaseClass : NSObject
@property(nonatomic) NSString* info;
@end

- (void)dealloc {
    self.info = nil;
}
@end
@interface SubClass : BaseClass
@property (nonatomic) NSString* debugInfo;
@end

@implementation SubClass

- (instancetype)init {
    if (self = [super init]) {
        _debugInfo = @"This is SubClass";
    }
    return self;
}
- (void)setInfo:(NSString *)info {
    NSLog(@"%@",[NSString stringWithString:self.debugInfo]);
}
- (void)dealloc {
    _debugInfo = nil;
}
@end

SubClassのインスタンスオブジェクトが破棄されると、まずサブクラスのdeallocが呼び出され、親クラスのdeallocを再呼び出す(これはinit初期化とは逆であり、ARCでは手動で呼び出す必要はない[super dealloc]).親クラスがdealloc時にaccessorを呼び出し、そのaccessorがクラスに書き換えられた場合、子クラスのaccessorが呼び出されます.この場合、サブクラスのdeallocが呼び出され、その完全な仮定に基づいて成立しないと、サブクラスのコードを実行するリスクがあり、前例のようにクラッシュします.
また、『Effective Objective-C 2.0高品質iOSとOS Xコードを記述する52の有効な方法』の第31条--dealloc方法では参照のみを解放し傍受を解除する一節において、著者らは以下の一節にも言及した: dealloc , , ( )。 , “ ”(Key-Value Observation,KVO) , (Observer) “ ” 。 , 。結論
以上、initおよびdeallocでaccessorを使用できないのは、オブジェクト向けの継承、マルチステート特性がaccessorによる副作用と結合するためである.継承およびマルチステートにより、親クラスの実装でaccessorを呼び出すと、サブクラスに書き換えられたaccessorを呼び出す可能性がありますが、サブクラス部分が完全に初期化されていないか、破棄されていないため、既存の仮定が成立せず、一連の論理的な問題が発生し、クラッシュすることもあります.より明確に述べるために、以下にinitおよびdeallocからそれぞれ例示する.
の最後の部分
Initとdeallocでaccessorを使用するのはリスクがあります.しかし、これは100%の崩壊や100%の誤りを意味するものではありません.現在の実験から,継承がある場合,initまたはdealloc法でaccessorを使用すると高いリスクがあるので,注意しなければならない.しかし、会社のプロジェクトでは、今コードに問題がなくても、将来のメンテナンスや拡張に問題が発生することはありません.アップルが言ったDon’t Use Accessor Methods in Initializer Methods and deallocをプログラミング規範としてこそ、この問題を根本的に回避することができる.ただし、初期化されるインスタンス変数がスーパークラスに宣言され、サブクラスでこのインスタンス変数にアクセスできない場合は、setterを使用してインスタンス変数に値を割り当てるしかありません.たとえば、インスタンス変数がlazy(怠惰ロード)の場合、getterメソッドでプロパティにアクセスする必要があります.そうしないと、インスタンス変数に値を割り当てることができません.したがって、絶対はありません.initメソッドとdeallocメソッドでaccessorを使用できない理由を理解してこそ、さまざまな状況で余裕を持つことができます.
文/VV木公子(作者)PS:特别な说明がなければ、すべての文章はオリジナル作品で、著作権は作者の所有で、転载は作者に连络して授権を得て、そして出典を明记して、すべての赏金はすべて本人の所有です!iOS开発者で、あるいはこの文章に兴味を持って、本人に関心を持って、后で更に多くの関连する文章を更新します!期待してください!
技術的な問題があれば、QQグループに参加して交流することを歓迎します.グループチャット番号:194236752.
参考記事
「Effective Objective-C 2.0高品質iOSとOS Xコードを記述する52の有効な方法」initとdealloc関数でaccessor Objective-Cを使用しない理由なぜinitまたはdeallocメソッドでaccessorメソッドiOSでdeallocメソッドを正しく処理できないのかなぜinitおよびdealloc関数でaccessor初期化およびdeallocメソッドで属性のアクセスメソッドを呼び出さずに直接呼び出すのかインスタンス変数