关于C#:为什么我不能在init方法中使用访问器方法

Why shoudn't I use accessor methods in init methods

从Apple有关内存管理的文档:

The only places you shouldn’t use accessor methods to set an instance variable are in init methods and dealloc. To initialize a counter object with a number object representing zero, you might implement an init method as follows:

To allow a counter to be initialized with a count other than zero, you might implement an initWithCount: method as follows:

1
2
3
4
5
6
7
- initWithCount:(NSNumber *)startingCount {
    self = [super init];
    if (self) {
        count = [startingCount copy];
    }
    return self;
}

为什么不 ?


看看我对这个问题的回答。

The main reason why is because a subclass might override your accessors and do something different. The subclass's accessors might assume a fully initialised object i.e. that all the code in the subclass's init method has run. In fact, none of it has when your init method is running. Similarly, the subclass's accessors may depend on the subclass's dealloc method not having run. This is clearly false when your dealloc method is running.

要扩展您的示例,如果您已经完成了

1
2
3
4
5
6
7
- initWithCount:(NSNumber *)startingCount {
    self = [super init];
    if (self) {
        [self setCount: [startingCount copy]];
    }
    return self;
}

但是一个子类已经重写了setCount:除了设置你的count变量之外还做一些事情,你可能会遇到麻烦。


JeremyP提出了一个很好的观点。由于setter调用的语法总是涉及"self",其类型是在运行时确定的,因此子类实例可以调用其重写的setter版本,如果"self"确实是一个子类对象...

但是,在某些情况下,您必须在初始化程序中使用setter。这是在超类中声明实例变量的时候;您无法直接访问实例变量,因此必须使用setter。

另一种情况是属性使用延迟初始化。在这种情况下,你必须通过吸气剂;如果不这样做,实例变量永远不会有机会被初始化。例如,EOCPerson类可能具有一个属性,可以访问代表每个人大脑的复杂对象。如果不经常访问此属性并且设置成本很高,则可以在getter中懒惰地初始化它,如下所示:

1
2
3
4
- (EOCBrain*)brain { if (!_brain) {
        _brain = [Brain new];
    }
return _brain; }

如果您直接访问实例变量并且尚未调用getter,则不会设置大脑,并且您必须调用访问器以访问大脑属性。

- 马特加洛韦的EOC书第7项


从OOP的角度来看(因为我没有使用objective-c的经验),你不会使用当前正在构造的对象的方法。这在某些语言中是可行的,但您已经可以访问私有变量。对象结构尚未完成,因此尚未准备好进行操作。一旦构造了对象,它就处于一个已知的良好状态(或者假设它)并且它可以执行。


在现代目标C中,您应该能够在init中使用访问器方法。如果几乎没有发生,编译器故障将很明显