swift初始化常见错误
importUIKit
前言:swift的初始化过程真的比oc的初始化难啊难,需要反反复复看。为了更好地理解、快速上手swift,记录一些常见的错误例子。
错误一:存储属性没有被初始化
- eg1:
class Student{
var name:String
}
报错:Class 'Student' has no initializers
- eg2:
class Student{
var name:String
init() {
}
}
报错:Return from initializer without initializing all stored properties
类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态,因此eg1和eg2报错都是因为属性没有被初始化造成的。我们可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。
class student {
var name:String
init() {
name = ""
}
}
class student {
var name = ""
}
错误二:在调用父类的初始化方法前,自身的存储属性未被初始化
class Student:NSObject{
var name:String
override init() {
super.init()
self.name = ""
}
}
报错:Property 'self.name' not initialized at super.init call
指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中 的构造器。一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器
必须保证它所在类引入的属性在它往上代理之前先完成初始化。而例子中我们却刚好违背了这一原则,student的name属性还未初始化就调用了父类的初始化。
纠正:
Student:NSObject{
var name:String
override init() {
self.name = ""
super.init()//可以省略,系统会自动调用
}
}
错误三:在调用父类的初始化方法前,自身的存储属性未被初始化并未调用其直接父类的的指定构造器
- eg1:
class student: NSObject {
init(name:String) {
super.init()
}
}
不报错
class view: UIView{
init(name:String) {
super.init()
}
报错:Must call a designated initializer of the superclass。
也许这是你会想到oc的它们的自定义构造:
@implementation student
- (instancetype)initWithname:(NSString *)name{
self = [super init];
if (self) {
}
return self;
}
@end
@implementation view
- (instancetype)initWithname:(NSString *)name{
self=[super init];
if (self) {
}
return self;
}
@end
嗯,嗯oc这里的确没毛病。而swift就不一样了,swift的指定构造器必须调用其直接父类的的指定构造器。注意了是:“直接”(重要的强调三遍)父类的指定构造器。eg1中student的直接父类构造器就是super.init()没毛病,而eg2中的view的父类直接构造器不是super.init(),它是 super.init(frame: CGRECT)
纠正:
init(name:String, frame:CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}//自定义view必须写上这个方法,即时我们不从xib或storboard加载view,要不然编译会报错
错误四:调用父类构造器之前访问了父类的属性
class person{
var age = 12
}
class student: person {
var name = ""
init(age:Int,name:String) {
self.age = 11
}
}
报错:'self' used in property access 'age' before 'super.init' call
指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的 新值将被父类中的构造器所覆盖。也就是在这个例子中,我们先访问了继承父类的age属性,系统后调用了super.init造成错误。
纠正:
class person{
var age = 12
}
class student: person {
var name = ""
init(age:Int,name:String) {
super.init()
self.age = 11
}
}
五给扩展类添加新的指定构造器或析构器
extension UIBarButtonItem{
init(age:String) {
}
}
报错:Designated initializer cannot be declared in an extension
扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。因此在为分类增加扩展的方法,我们需要提供便利构造器。
纠正:
extension UIBarButtonItem{
convenience init(age:String) {
let btn = UIView()
self.init(customView: btn)
}
}