父/子关系的Swift:Init()模式 安装程序

父/子关系的Swift:Init()模式 安装程序,swift,initialization,Swift,Initialization,假设我有两个班: final class Parent: NSObject { var child: Child } final class Child: NSObject { weak var parent: Parent? init(parent: Parent) { self.parent = parent } } 问题: 现在假设我想实例化一个子并在Parent的init()方法中建立这个关系。我会这样做: final class

假设我有两个班:

final class Parent: NSObject 
{
   var child: Child
}


final class Child: NSObject 
{
   weak var parent: Parent?

   init(parent: Parent) 
   {
      self.parent = parent
   }
}

问题: 现在假设我想实例化一个子
并在
Parent
init()
方法中建立这个关系。我会这样做:

final class Parent: NSObject 
{
   var child: Child

   init() 
   {
      child = Child.init(parent: self)    // ERROR!
   }
} 
斯威夫特抱怨说我在
super.init()
之前使用了
self
。如果我在实例化
child
之前放置
super.init()
,Swift抱怨说
child
super.init()
调用中没有赋值

我一直在使用隐式展开选项来关闭Swift,如下所示:

final class Parent: NSObject 
{
   var child: Child!

   init() 
   {
      super.init()
      child.init(parent: self)
   }
}
我的问题是:在这种情况下人们会做什么?这是/曾经是Objective-C中非常常见的模式,Swift只会让人头痛。我知道我可以将
parent
的赋值从
Child
init()
中分离出来,这样我就可以
init()
Child,调用
super.init()
,然后分配
parent
,但在我遇到这种情况的现实世界中,这不是一个选项。它也是丑陋和臃肿的

我确实意识到Swift的意图是在对象完全初始化之前阻止使用它。而且
child
init()
可以回调到
parent
并访问尚未设置的状态,这对于隐式展开的可选方法来说是一种危险


我在这里找不到任何关于Swift最佳实践的指导,所以我想问人们如何解决这个鸡和蛋的问题。将属性转换为隐式展开的选项真的是绕过此限制的最佳方法吗?谢谢。

你可以让它变得懒惰
,你所有的头痛都会过去的

final class Parent {
    lazy var child = Child(parent: self)
}


final class Child {
    weak var parent: Parent?
    
    init(parent: Parent) {
        self.parent = parent
    }
}
在Swift中,应在
self
可用之前初始化所有存储的属性。在
超类
中包括存储的属性。所以Xcode直到那时才允许您使用
self
。但是使用
lazy
意味着它之前已初始化。因此,您可以自由使用
self

一些有用的注释:
  • 如果您的类不需要
    objc
    高级功能,请不要从
    NSObject
    继承
  • 在swift命名约定中,开括号
    将在objective-C中读作
    with
    。因此您不需要在初始值设定项中使用
    with parent
    作为标签
  • 按照约定使用参数的内部和外部命名

  • 还要注意的是,上面的一些注释是在注释之前写的,你可以让它变得懒惰,你所有的烦恼都会消失

    final class Parent {
        lazy var child = Child(parent: self)
    }
    
    
    final class Child {
        weak var parent: Parent?
        
        init(parent: Parent) {
            self.parent = parent
        }
    }
    
    在Swift中,所有存储的属性都应该在
    self
    可用之前初始化。将存储的属性包括在
    超类中
    。因此Xcode在此之前不允许您使用
    self
    。但是使用
    lazy
    意味着它之前就已经初始化。因此,您可以自由使用
    self

    一些有用的注释:
  • 如果您的类不需要
    objc
    高级功能,请不要从
    NSObject
    继承
  • 在swift命名约定中,开括号
    将在objective-C中读作
    with
    。因此您不需要在初始值设定项中使用
    with parent
    作为标签
  • 按照约定使用参数的内部和外部命名

  • 还请注意,上面的一些注释是在注释之前编写的

    为什么要从
    NSObject
    继承?可能有帮助:,。在您的情况下,这将是
    lazy var child=child(withParent:self)
    因为,在我非平凡的现实世界中,我使用从NSObject继承的类,而这些类需要从某些东西继承才能使用super.init()。我已经将我的示例简化为我可以组装的最小情况。懒惰并不是我想要的。我希望“child”从“parent”开始就存在;我不想等到我第一次打电话给“child”来设置它。Joe Groff在Swift论坛的一篇文章中写道:“IUO历来是处理循环引用的答案……现在,一个属性包装器……可能是最好的方式。”为什么要从
    NSObject
    继承?可能有帮助:,。在您的情况下,这将是
    lazy var child=child(withParent:self)
    ,因为在我的非平凡的实际情况中,我使用的类是从NSObject继承的,并且这些类需要从super.init()的某些内容继承需要。我已经把我的例子浓缩到了我能收集到的最起码的案例。懒惰并不是我想要的。我希望“孩子”从“家长”一开始就存在;我不想等到我第一次打电话给“孩子”来建立它。来自Swift论坛a中的Joe Groff:“IUO在历史上一直是处理循环引用的答案,……现在,属性包装器……可能是最好的方式。”同样,我有很好的理由希望在创建“child”时它存在。我不希望它延迟到我第一次调用它。例如,child的“init()”的其余部分方法分派一个异步请求以从远程服务器下载一些数据。我希望该过程立即启动。将“\u=self.child”添加到父级的“init()”底部以启动lazy属性似乎很愚蠢。最常见的情况是具有“init(delegate:)”的情况方法。我通常希望代理是我,我正在尝试在我自己的“init()中设置它方法.Delegate是一个
    弱的
    属性。您可以将parent设置为可选的,并且可以在初始值设定项中自由使用self。但我认为这不是您想要的。同样,我有很好的理由希望在创建“child”时让它存在。我不希望它延迟到第一次调用它。例如,child的其余部分的“init()”方法调度一个异步请求以从远程下载一些数据