ios 为什么我可以初始化一个没有参数的UIView,但它的文档却没有一个空的初始化器?

qqrboqgw  于 2023-03-31  发布在  iOS
关注(0)|答案(6)|浏览(150)
let view = UIView()

为什么只有UIView初始化器是init(frame: CGRect)时,编译没有错误?
具体来说,我试图写一个继承自UIView的新类,但这段代码抛出了一个错误:

class SquadHorizontalScrollViewCell: UIView {

    init(var: FIRDataSnapshot){
        super.init()
....

它说它必须调用指定的初始化器。

nkkqxpd9

nkkqxpd91#

UIView继承自UIResponder,而UIResponder继承自NSObject
NSObject.init()initializer可以在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也可以与子类中定义的其他指定初始化器一起编译,因为它的指定初始化器在编译时是未知的。根据docs,如果以编程方式示例化,则init(frame:)是指定初始化器,否则init()是指定的初始化器。这意味着UIView继承了NSObject指定的初始化器,而不是像上面的例子那样替换它。
在您的示例中:

class SquadHorizontalScrollViewCell: UIView {

    init(var: FIRDataSnapshot){
        super.init()

我们看到指定的初始化器是init(frame: CGRect),所以你必须调用这个指定的初始化器。

jckbn6z7

jckbn6z72#

指定的初始化器应该调用它的超类指定的初始化器。
在这种情况下,super.init()NSObject的指定初始化器,而不是UIView。调用UIResponder init是UIView的责任,我猜它没有指定的初始化器,因此UIView将在其init(frame:CGrect)初始化器中调用Super.init。检查“初始化器委派”
为什么let x = UIView()是可以的,这是因为
与Objective-C中的子类不同,Swift子类默认情况下不会继承它们的超类初始化器。Swift的方法防止了这样一种情况,即超类中的简单初始化器被更专门的子类继承,并用于创建未完全或正确初始化的子类的新示例。
由于UIView是目标c类,它仍然可以这样做。但您将无法调用SquadHorizontalScrollViewCell(),除非您没有提供任何初始化器或您覆盖了超类(UIView)的指定初始化器
查看此link了解更多信息

rqcrx0a6

rqcrx0a63#

对于UIViewinit(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

hyrbngr7

hyrbngr74#

在某个时候,你的视图需要初始化一些东西,这就是为什么编译会抱怨,因为它找不到如何开始初始化你的自定义视图。因为在最后,一个视图将从一个xib(init(coder aDecoder: NSCoder)),或从一个frame(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)
}

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

mxg2im7a

mxg2im7a5#

你会注意到,如果你创建了自己的UIView子类,只使用log语句覆盖init(frame:),然后只使用init()示例化这个新类,你的init(frame:)实际上是用一个零大小的框架调用的。所以指定的初始化器仍然会被调用。

kqhtkvqz

kqhtkvqz6#

我想补充一点,如果你在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)来纠正这个问题。

相关问题