【问题标题】:Should I use an accessor method in delegated method from init?我应该在初始化的委托方法中使用访问器方法吗?
【发布时间】:2011-08-11 18:00:44
【问题描述】:

我应该使用访问器方法还是直接访问 iVar?:

- (id)initWithData: (NSData *)someData;
{
    if (self = [super init]) {
        NSXMLParser *parser = [[NSXMLParser alloc] initWithData: someData];
        [parser setDelegate: self];
        [parser parse];
        [parser release];
    }
    return self;
}

- (void)parser: (NSXMLParser *)parser didStartElement: (NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName: (NSString *)qName attributes: (NSDictionary *)attributeDict
{
    [self setValue: someValue]; // Setting an iVar of self
    [[self value] setOtherValue: otherValue]; // Setting an iVar of an object that's an iVar of self
}

【问题讨论】:

  • 与什么相反,直接访问 ivars?这并不重要,但问题在这里并不明确。
  • 与直接访问它们相反,是的。对不起,我没有说清楚。我编辑了问题。

标签: objective-c init accessor


【解决方案1】:

除非您非常小心,否则这是不安全的。想象以下场景:

@interface BaseClass : NSObject
@property (readwrite) NSInteger twists;
@end

@interface SubClass : BaseClass {
    struct something *aField;
}
@end

@implementation BaseClass
- (id)init {
    self = [super init];

    if (self) {
        self.twists = 100;
        // or
        [self setTwists:100];
    }

    return self;
}

@synthesize twists;
@end

@implementation SubClass
- (id)init {
    self = [super init];

    if (self) {
        aField = malloc(sizeof(struct something));
    }

    return self;
}

- (void)dealloc {
    free(aField);
    [super dealloc];
}

- (void)setTwists:(NSInteger)value {
    [super setTwists:value];
    aField->twists++;
}
@end

如果使用SubClass,则在实例化时将调用-setTwists: 的实现,这将取消引用aField。此时,它的初始化程序还没有执行;它的超类的初始化程序仍在执行。程序将在 NULL 指针取消引用时崩溃。

【讨论】:

  • 这种对安全性的关注是公平的,专门针对从初始化程序中调用访问器的情况,而且这个问题并不是 Objective-C 独有的。例如,Java 中的最佳实践经常禁止从构造函数调用任何非最终实例方法,只是因为子类可能会覆盖这些方法并做一些带有奇怪副作用的事情的危险。但是,我认为这些问题一般不应该阻止在类中普遍使用访问器。
  • -twistWidget 是什么意思?你的意思是setTwists:
  • 吉姆:哎呀,是的。我中途更改了方法的名称。
  • @Tim:他们不应该阻止它,不——事实上,这是使用访问器的原因,让子类进入系统并让它们修改行为。但在初始化器和释放器期间,应该避免它们。
  • 即便如此,也不叫直接吧?我自己不打电话给parser:didStartElement:,它是通过[parser parse]; 调用的,它在init 本身内开始和结束。
【解决方案2】:

作为一般规则,我相信您应该这样做。在@property 定义和一些用于内存管理的新ARC 东西出现之前,它可能更具争议性。但是,在头文件中使用诸如“@property (retain)”之类的声明确实有助于确保正确遵循保留/释放约定,甚至在 ARC 的情况下也是自动化的。

【讨论】:

  • 但如果它在init 本身内,那么我不应该使用访问器方法吗?
  • 我想说,在初始化器中对 self 使用访问器之前,您应该谨慎行事。如果您非常确信该类不会被子类化,那么就不用担心了。如果您选择不使用访问器,请记住您有责任确保正确保留/释放。如果您不使用自动生成的访问器,那么您就会失去这一点。
  • 很抱歉让我很困惑,但如果我理解正确,委托方法是否从init 中调用并不重要?它直接执行(字面意思是在init 内)
  • 在 self 以外的对象上使用访问器不会有同样的危险。所以 init 方法中的 [parser setDelegate:self] 非常好。唯一的潜在危险是,如果您在 init 方法中执行 [self setValue:someValue] 之类的操作,因为 setValue 方法可能会被覆盖并在从初始化程序调用时遇到问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
  • 2011-03-26
  • 1970-01-01
相关资源
最近更新 更多