为啥在我的结构中调用 self.init 之前使用了错误“self”?

Posted

技术标签:

【中文标题】为啥在我的结构中调用 self.init 之前使用了错误“self”?【英文标题】:Why am I getting the error 'self' used before self.init call in my struct?为什么在我的结构中调用 self.init 之前使用了错误“self”? 【发布时间】:2017-05-25 12:34:07 【问题描述】:

我已经构建了这个struct 来处理我计划在一些自定义类中使用的特定类型的数据。 我的问题是 featureSubSet 变量能够成为少数枚举之一,当这个结构被初始化时,它不知道它将是哪个枚举,所以我将它声明为 Any。

当调用公共 init 时,它会适当地将数据汇集到所需的私有 init 方法中,以便它可以正确且完全地初始化。

我在公共 init 方法结束时收到错误,但我不知道如何让它消失。

struct Feature 
//MARK: Variables needed for Feature
var featureSet: FeatureType
var featureSubSet: Any
var effect: String
var active: Bool?
var skill: Skill?
var ability: Ability?

public init(base: String, sub: String, effect: String, skill: Skill? = nil, ability: Ability? = nil) 
    switch base 
    case featureCategoryList()[0]: // Character Features
        self.init(CharacterFeature: sub, effect: effect)
    case featureCategoryList()[1]: // Combat Features
        self.init(CombatFeature: sub, effect: effect)
    case featureCategoryList()[2]: //Skill Features
        guard let newSkill = skill else 
            print("No skill")
            return
        
        self.init(SkillFeature: sub, effect: effect, skill: newSkill)

    default:
        print("Somehow you picked something not on the list.")
        break
    


private init(CharacterFeature sub: String, effect: String) 
    self.featureSet = .CharacterFeatures
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)!
    self.effect = effect


private init(CombatFeature sub: String, effect: String) 
    self.featureSet = .CombatFeatures
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)!
    self.effect = effect


private init(SkillFeature sub: String, effect: String, skill: Skill) 
    self.featureSet = .SkillFeatures
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)!
    self.skill = skill
    self.effect = effect


//MARK: Feature functions
func string() -> String 
    //TODO: Make a string output for what the feature is.
    return ""


【问题讨论】:

你遇到了什么错误? 【参考方案1】:

这是一种不正确的方法。如果您发现自己将某些内容存储为Any,则几乎可以肯定您走错了路。探索这段代码很困难,因为它定义了许多其他类型,但是看着它,你不清楚为什么在这里需要AnyfeatureSubSet 始终是 CharacterFeatures 类型。枚举的所有值都是相同的“类型”。

这里有一个错误:

    case featureCategoryList()[2]: //Skill Features
        guard let newSkill = skill else 
            print("No skill")
            return
        

这里:

    default:
        print("Somehow you picked something not on the list.")
        break
    

这会在没有初始化 self 的情况下返回(这可能是您的问题)。如果您可能失败,那么您需要将其设为可选初始化程序 (init?) 或抛出初始化程序,或者您需要在出错时调用 fatalError() 或类似的崩溃方法。你不能不初始化就返回。

不过,我强烈建议您重新设计它。我看不出参数应该是字符串的任何理由。与其传入"character" 并在某个表中查找,不如让调用者将.character.combat 作为枚举传递(您已经拥有枚举)。

注意,在 Swift 3 中,枚举大小写应始终以小写字母开头。

有点难以理解代码中发生了什么,但它看起来根本不应该是一个结构。它看起来像一个枚举。像这样的:

enum CharacterFeature 
    // Features for characters


enum CombatFeature 
    // Features for combat


enum Skill 
    // skills


enum Effect 
    // effects


enum Feature 
    case character(CharacterFeature, Effect)
    case combat(CombatFeature, Effect)
    case skill(Skill, Effect)

或者,您可以在此处使用结构,尤其是当所有功能共享更多内容时:

struct Feature 
    enum Kind 
        case character(CharacterFeature, Effect)
        case combat(CombatFeature, Effect)
        case skill(Skill, Effect)
    

    let kind: Kind
    let effect: Effect

关键是您通常应该使用显式类型,而不是字符串。如果您发现自己需要的选项太多,那么您经常做错事。

【讨论】:

很好的答案!我只会添加使用String 的选项,作为枚举原始类型,如果他需要保留那些(讨厌的!)字符串用于其他目的 - 并且他希望对指定的命名进行一些控制使用的约定。 我其实完全同意。谢谢你的例子。不幸的是,这个错误让我发疯了,所以我还没有完成 switch 语句和私有 init 方法,但我使用 Any 作为 featureSubSet 的数据类型的原因是因为它可能是四种不同的枚举类型之一。使用您的替代方案,您建议在获取用户输入时如何处理初始化?我正在使用字符串通过适当的 init 方法序列对其进行过滤以完全初始化它,有什么更好的方法来做到这一点? 一般处理用户输入的方法是使枚举RawRepresentableString。因此,您使用字符串初始化Kind,然后将其传递给Feature。这比将大量字符串传递给Feature 包含更多内容。如您所见,您避免使用枚举上的关联数据Any。如果您可以举一个输入数据的示例以及您希望得到的结果,我相信我们可以构建一个示例。 好吧,用户将看到三到四个文本字段。两个或三个取决于第一个的结果,将通过 Popover 呈现一个 tableview 以选择一个选项。但是一个必须是一个字符串。 Effect 最终将成为一个接收字符串并将其转换为掷骰子计算公式的类。第一个输入将指示“种类”,第二个输入将指示“特征”,如果“特征”属于某些类型,它将为您提供另一个类别可供选择。因此,所涉及的数据类型将是 2 到 3 个枚举和一个字符串。我不确定这是否能更好地解释它。【参考方案2】:

假设featureCategoryListstruct Feature 函数,你原来的错误:

selfself.init 调用之前使用

可能来自以下几行:

case featureCategoryList()[0]: 
    ...
case featureCategoryList()[1]:
    ...
case featureCategoryList()[2]:

因为您在初始化之前隐式调用了self

修复上述错误后,您需要修复这些错误:

self.init 在从初始化程序返回之前不会在所有路径上调用

查看 Rob 的回答,了解一些不错的选择。

【讨论】:

以上是关于为啥在我的结构中调用 self.init 之前使用了错误“self”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在我的视图被推送到导航堆栈之前我的 UIAlertView 没有在屏幕上消失?

为啥我应该在我的代码中尽量减少系统调用的使用?

为啥当我使用 presentModelViewController 时未在我的 UITabBArController 中调用 viewWillAppear?

为啥我不能在我的结构中打印名称?

为啥我不能调用我的结构信号量方法?

为啥我不能在我的回调中调用我的 vue 组件的函数[关闭]