为什么我可以在没有参数的情况下初始化 UIView,但它的文档没有空的初始值设定项


let view = UIView()

为什么当唯一记录的UIView初始值设定项init(frame: CGRect)时编译而没有错误?

具体来说,我正在尝试编写一个继承自UIView的新类,但这段代码抛出了一个错误:

class SquadHorizontalScrollViewCell: UIView {
    init(var: FIRDataSnapshot){
        super.init()
....

它说它必须调用指定的初始值设定项。

UIView继承

UIResponder,继承自NSObject

NSObject.init()初始值设定项可以在UIView上访问,但在替换这个指定初始值设定项的NSObject子类上则不能访问。

让我们考虑一个例子。

class A: NSObject { 
    init(_ string: String) { } 
}

这会导致 let a = A() 的编译器错误 - 缺少初始值设定项的参数 #1,因为此初始值设定项替换了子类中NSObject的指定init()初始值设定项。

您只需要将子类的初始值设定项定义为convenience初始值设定项:

class A: NSObject { 
    convenience init(_ string: String) { 
        self.init() // Convenience initializers must call a designated initializer.
    } 
}

let a = A()现在编译。

UIView还可以使用子类中定义的其他指定初始值设定项进行编译,因为在编译时不知道其指定的初始值设定项。根据文档,如果以编程方式实例化,则init(frame:)是指定的初始值设定项,否则init()是指定的初始值设定项。这意味着UIView继承NSObject指定的初始值设定项,而不是像上面的示例中那样替换它。

在您的示例中:

class SquadHorizontalScrollViewCell: UIView {
    init(var: FIRDataSnapshot){
        super.init()

我们看到指定的初始值设定项是 init(frame: CGRect) ,因此您必须改为调用此指定的初始值设定项。

指定的初始值设定项应调用其超类指定的初始值设定项。

在这种情况下,super.init()NSObject 而不是 UIView 的指定初始值设定项。调用UIResponder init是UIView的责任,我想它没有指定的初始值设定项,因此UIView将在其init(frame:CGrect)初始化器中调用Super.init。选中"初始值设定项委派"

为什么let x = UIView()没问题,就是因为这个

与 Objective-C 中的子类不同,Swift 子类不会继承 默认情况下,它们的超类初始值设定项。斯威夫特的方法防止了 从超类继承简单初始值设定项的情况 由更专业的子类,用于创建 未完全或正确初始化的子类。(苹果(

但是你将无法调用SquadHorizontalScrollViewCell((,除非你没有提供任何初始值设定项,或者你覆盖了超类(UIView(的指定初始值设定项。


查看此链接以获取更多信息

对于UIView init(frame: CGRect)是默认的初始值设定项。您必须在初始化实例变量后调用它。如果从NIB查看,则调用init?(coder aDecoder: NSCoder)而不是init(frame: CGRect)。因此,在这种情况下,您必须awakeFromNib()方法中初始化实例变量。在这种情况下,您的代码应如下所示:

class SquadHorizontalScrollViewCell: UIView {
    init(firDataSnapshot: FIRDataSnapshot){
        // intialize your instance variable
        super.init(frame: CGRectZero) // set your expected frame. For demo I have set `CGRectZero`
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

有关更多信息,您可以查看此链接 https://developer.apple.com/reference/uikit/uiview

在某个时候,您的视图将需要与某些内容一起初始化,这就是编译抱怨的原因,因为它找不到如何开始自定义视图的初始化。因为最后,视图将从 xib ( init(coder aDecoder: NSCoder) ( 或从帧 ( init(frame: CGFrame) ( 初始化。所以在这里,最简单的方法是至少在自定义 init 方法中调用super.init(frame: CGRectZero)

init (var: FIRDataSnapshot) {
        super.init(frame: CGRectZero)   
}
// This method below is always needed when you want to override your init method
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

但是您仍然需要设置框架的大小等。

您会

注意到,如果您创建自己的UIView子类并且仅使用log语句覆盖init(frame:),然后仅使用init()实例化这个新类,那么您的init(frame:)实际上是用零大小的帧调用的。因此,指定的初始值设定项仍在被调用。

我想

在其他答案中添加另一个方面。如果你在 Swift 中实现了 UIView 的子类,并且没有提供init(frame:)初始化器,则会在运行时抛出异常:

Fatal error: Use of unimplemented initializer 'init(frame:)' for class 'x_swiftlang_01Tests.U'

当您添加如下所示的初始化器时,您将获得一个有趣的日志输出:

func testU() throws {
    class U: UIView {
        let s: String
        required init?(coder: NSCoder) {
            return nil
        }
        // Without init(frame:) an exception is thrown:
        // "Fatal error: Use of unimplemented initializer 'init(frame:)' for class 'x_swiftlang_01Tests.U'"
        override init(frame: CGRect) {
            self.s = "framed"
            super.init(frame: frame)
        }
        init(s: String) {
            self.s = s
            super.init()
            print("(#function): init:s=(s)")
        }
        deinit { print("(#function): deinit:s=(s)") }
    }
    let u = U(s: "u")
}

输出:

init(s:): init:s=u
deinit: deinit:s=framed

基本上将调用两个初始化器,第二个初始化器将覆盖第一个初始化器设置的任何内容。不要打电话给super.init(),但super.init(frame: .zero)纠正这一点。

最新更新