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)
纠正这一点。