Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/95.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Swift/iOS中使用前初始化类属性_Ios_Swift - Fatal编程技术网

在Swift/iOS中使用前初始化类属性

在Swift/iOS中使用前初始化类属性,ios,swift,Ios,Swift,我很难掌握实例化变量的正确方法,这些变量总是需要在对象完全运行之前设置,但可能需要在构造函数之后实例化。基于Swift的其他约定和限制,似乎有一种设计模式我不知道 以下是我的用例: 我有一个从UIViewController继承的类,它将基于用户操作以编程方式创建视图 我需要将这些视图附加到此类,但要这样做,我需要根据另一个控制器提供的配置数据检索它们的内容 我不在乎这个配置数据是传递给构造函数(在这种情况下,它总是必需的)还是在使用它之前通过对这个对象的二次调用提供的 我的问题似乎是子弹3

我很难掌握实例化变量的正确方法,这些变量总是需要在对象完全运行之前设置,但可能需要在构造函数之后实例化。基于Swift的其他约定和限制,似乎有一种设计模式我不知道

以下是我的用例:

  • 我有一个从UIViewController继承的类,它将基于用户操作以编程方式创建视图
  • 我需要将这些视图附加到此类,但要这样做,我需要根据另一个控制器提供的配置数据检索它们的内容
  • 我不在乎这个配置数据是传递给构造函数(在这种情况下,它总是必需的)还是在使用它之前通过对这个对象的二次调用提供的
我的问题似乎是子弹3中的两种方法都有缺陷

在第一种情况下,只有一个合法的构造函数可以调用这个类,但我不得不重写其他构造函数,并用假值初始化成员变量,即使其他构造函数从未打算使用(我还试图根据Swift的最佳实践将这些变量保留为
let
类型)

在第二种情况下,我有效地将构造函数分为两部分,并引入一个额外的失败点,以防在使用类之前调用第二部分失败。我也不能将第二部分移到保证在使用之前调用的方法(例如viewDidLoad),因为我仍然需要从配置中传入其他参数。虽然我可以确保手动调用
initPartTwo
,但我更希望有一种机制能够更好地将其与实际构造函数分组。我不可能是第一个遇到这种情况的人,似乎有一种模式我没有看到,使这个更干净

更新: 我最终选择了matt建议的模式的修改版本:

struct Thing {
    let item1: String
    let item2: String
    struct Config {
        let item3: String
        let item4: String
    }

    var config:Config! {
        willSet {
            if self.config != nil {
                fatalError("tried to initialize config twice")
            }
        }
    }

    init() {
        self.item1 = ...
        self.item2 = ...
        ...
    }

    public func phaseTwoInit(item3: String, item4: String) {
        self.item3 = item3
        self.item4 = item4
        ...
    }
}

var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t

如果在对象初始化时无法提供初始实例变量属性值,通常的做法是将其声明为可选。这样,它就不需要由类的初始值设定项初始化(它有一个值-它自动为
nil
),另外,您的代码随后可以区分未初始化(
nil
)和已初始化(而不是
nil

如果是可选的,如果是隐式展开的可选,则此安排不需要对代码产生特殊影响(即,它不必添加展开)

如果您的反对意见是,由于现在必须使用
var
声明该实例变量,因此您被迫打开该实例变量的多个设置的门,那么请使用setter观察器关闭该门:

struct Thing {
    var name:String! {
        willSet {
            if self.name != nil {
                fatalError("tried to set name twice")
            }
        }
    }
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash

请参阅我的书的第二部分,其中我讨论了如何“延迟变量的初始化”,但是所有对该变量的引用都必须使用问号。实际上,以相同方式(但在不同时间)实例化的两个变量的访问方式将非常不同。这真的是最佳解决方案吗?如果这些是隐式展开的选项,则不是。如果您有疑问,您仍然可以检查
nil
,但您可以不带问号和感叹号地引用它们。请尝试实际阅读我向您介绍的书中的部分。我用例子讨论了所有这些。我读过它,我喜欢你描述的隐式解包可选解决方案,但我仍然有点困惑,因为它似乎与斯威夫特的另一个咒语/惯例相矛盾,即总是使用
let
来声明不会更改的变量。对于隐式可选变量,我必须使用
var
,即使我只设置了一次变量(尽管不是在构造函数中)。这似乎有些奇怪,因为Swift-even特意对使用
let
声明的常规成员变量做了这样的例外,允许它们在
init
完成之前保持
nil
,所以我很惊讶隐式展开的选项与
let
不兼容