将成员初始化为 class 函数导致 'self' used in method call 错误

Initialising member to class function causes 'self' used in method call error

我有一个指向 class 函数之一的 class 属性。但是,当我尝试使用其中一个函数初始化此变量时,出现以下错误:

'self' used in method call before all stored properties are initialized.

我可以为这些函数初始化任何其他变量,但错误使它听起来像我正在调用函数,即使我没有。

import UIKit
import AudioToolbox

class BeatMaker {

    // iPhone 7 and up use beat function, iPhone 6s use beatFallback
    let hapticFunction: () -> ()
    let impactGenerator = UIImpactFeedbackGenerator.init(style: .heavy)

    init(supportsImpactGenerator: Bool) {
        // error 1: 'self' used in method call 'beat' before all stored properties are initialized
        // error 2: 'self' used in method call 'beatFallback' before all stored properties are initialized
        self.hapticFunction = (supportsImpactGenerator) ? beat : beatFallback
    }

    private func beat() {
        impactGenerator.impactOccurred()
    }

    private func beatFallback() {
        AudioServicesPlaySystemSound(1520)
    }

    func makeABeat() {
        hapticFunction()
    }
}

在这种特定情况下,我想利用 Taptic Engine 并通过 UIImpactFeedbackGenerator 模拟点击。 iPhone 6s 不支持这个引擎,所以我想调用一个产生类似效果的回退函数。

我也试过当场初始化变量:

// this works
var hapticFunction: (BeatMaker) -> () -> () = beat

init(supportsImpactGenerator: Bool) {
    if !supportsImpactGenerator {
        // error: Cannot assign value of type '() -> ()' to type '(BeatMaker) -> () -> ()'
        self.hapticFunction = beatFallback

        // produces same error
        self.hapticFunction = beatFallback.self
    }
}

我知道我可以将所有内容设为静态或将所有内容从 class 中移除,但是 感觉 应该可以,但实际上没有。我错过了什么吗?

编辑

hapticFunction 的type of type 设置为optional 似乎可行,但这对我来说没有任何意义。有什么区别?

// this works
var hapticFunction: (() -> ())?

init(supportsImpactGenerator: Bool) {
    self.hapticFunction = (supportsImpactGenerator) ? beat : beatFallback
}

有两种方法可以解决这个问题

您可以:

hapticFunction一个初始值,比如{}

或修正为:

class BeatMaker {

    let impactGenerator = UIImpactFeedbackGenerator.init(style: .heavy)
    let supportsImpactGenerator: Bool

    init(supportsImpactGenerator: Bool) {
        self.supportsImpactGenerator = supportsImpactGenerator
    }

    private func beat() {
        if supportsImpactGenerator {
            impactGenerator.impactOccurred()
        } else {
            AudioServicesPlaySystemSound(1520)
        }
    }

    func makeABeat() {
        beat()
    }
}

最好不要使用 Bool,而是使用嵌套的 Enum,如果您想稍后添加其他一些触觉反馈模式,它也更具可扩展性。

我有一个通用的解决方案来解决你的问题。所以要么你这样做:


public class FunctionOwner {

    private let mode: Mode

    public init(`do` mode: Mode = .default) {
        self.mode = mode
    }
}


public extension FunctionOwner {

    enum Mode {
        case foo, bar
    }

    func fooOrBar() {
        switch mode {
        case .foo: foo()
        case .bar: bar()
        }
    }
}

private extension FunctionOwner {
    func foo() {
        print("doing foo")
    }

    func bar() {
        print("doing bar")
    }
}

public extension FunctionOwner.Mode {
    static var `default`: FunctionOwner.Mode {
        return .foo
    }
}

// USAGE
FunctionOwner(do: .bar).fooOrBar() // prints "doing foo"
FunctionOwner(do: .foo).fooOrBar() // prints "doing bar"

或者如果您出于某种原因确实想保留存储的 Mode,您可以这样做(可能与您的实际问题有关,即如何在初始化。):

public class FunctionOwner {

    private let _function: (FunctionOwner) -> Void

    public init(`do` mode: Mode = .default) {
        _function = { functionOwner in
            switch mode {
            case .foo: functionOwner.foo()
            case .bar: functionOwner.bar()
            }
        }
    }
}

public extension FunctionOwner {

    enum Mode {
        case foo, bar
    }

    func fooOrBar() {
        _function(self)
    }
}

// The rest of the code is the same as the example above