在 Swift class 的指定初始化器中调用实例方法

Calling an instance method in designated initializer of a Swift class

在Swift编程语言中,

是强制性的

“A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.” source of quote from another s/o

否则会显示类似下面的错误

Property 'self.baz' not initialized at super.init call

我想在 Swift 中实现以下 Objective-C 代码:

@interface FooBar : NSObject

@property (nonatomic, readonly) NSString *baz;

@end

@implementataion FooBar

@synthesize baz = _baz;

- (instancetype)initWithString:(NSString *)string {
    self = [super init];
    if (self) {
        _baz = [self parseString:string];
    }
    return self;
}

- (NSString *)parseString:(NSString *)string {
    // Perform something on the original string
    return ...;
}

@end

以下实现会引发上述编译器错误:

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        super.init()
        baz = parseString(string)
    }

    private func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return ...;
    }
}

如果我执行以下操作,我会收到一条错误消息

Use of self in method call 'parseString' before super.init initialises self

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        baz = parseString(string)
        super.init()
    }

    private func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return string;
    }
}

所以我目前的解决方案是使用私有 class 方法:

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        baz = FooBar.parseString(string)
        super.init()
    }

    private class func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return ...;
    }
}

我的问题是是否有更好的方法来实现相同的目的,或者私有 class 方法是最好的方法吗?

正如您正确提到的那样,编译器会抱怨 "Use of self in method call 'parseString' before super.init initialises self"。这样做的原因是编译器必须确保您在完成初始化之前不会访问 self 上的任何内容。因为编译器不想(甚至不能)在初始化完成之前检查每个传出方法调用,所以它只是不允许在初始化完成之前使用任何 self。

幸运的是,您的方法 parseString 不想更改或访问任何 属性。因此你可以并且应该使它成为一个非实例函数。如果它与 FooBar 的实例无关,为什么它只能在 FooBar 的实例上可用?

您可以简单地将其设为 staticclass 函数,就像您已经做的那样,这非常好。

但我会更进一步,将其完全移出 FooBar。有几个选项:

  • 创建一个 class Helper 并将其定义为 class function
  • 创建一个 class Helper 并将其定义为 instance function 并在 class FooBar 中创建一个 Helper 实例或创建一个全局的,并在 init 中也传递那个。