swift蛋疼的初始化过程

本文深入探讨Swift中的构造过程,包括两阶段构造的概念及其背后的安全检查机制。通过具体示例展示了如何正确实现构造器,避免常见的错误陷阱。

转自:https://segmentfault.com/a/1190000002392828

阶段构造

Swift 的构造过程分为两个阶段:

  • 第一个阶段,每个存储型属性通过引入自己的构造器来设置初始值。
  • 第二个阶段,在新实例准备使用之前进一步定制存储型属性。

安全检查

在构造的过程中, Swift 会进行四种安全检查。

安全检查 1

指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。

比如下面这段代码就是错误的:

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
}


class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        super.init(name: name)  // ERROR!
        self.quantity = quantity
    }
}

安全检查 2

指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。

比如下面这段代码就是错误的:

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
}


class RecipeIngredient: Food {
    override init(name: String) {
        self.name = "WHY"   // ERROR!
        super.init(name: name)
    }
}

安全检查 3

便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。

比如下面这段代码就是错误的:

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        quantity = 2    // ERROR!
        self.init(name: name, quantity: 1)
    }
}

安全检查 4

构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 的值。

比如下面这段代码就是错误的:

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        println(self.name)  // ERROR!
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}

蛋疼的初始化过程

Swift 的初始化过程十分严格,刚从 OC 转过来的同仁可能习惯性的就写了这样的代码:

class ViewController: UIViewController {
    private let animator: UIDynamicAnimator

    required init(coder aDecoder: NSCoder) {
        // ERROR: Property 'self.animator' not initialized at super.init call
        super.init(coder: aDecoder)
        animator = UIDynamicAnimator(referenceView: self.view)
    }
}

是的它报错了!哦想起来了,要在 super 前面初始化自己的属性:

class ViewController: UIViewController {
    private let animator: UIDynamicAnimator

    required init(coder aDecoder: NSCoder) {
        // use of property 'view' in base object before super.init initializes it
        animator = UIDynamicAnimator(referenceView: self.view)
        super.init(coder: aDecoder)
    }
}

是的它又报错了!哦想起来了你不能在 view 的父类初始化之前调用这个属性。那就先不赋值吧:

class ViewController: UIViewController {
    private let animator: UIDynamicAnimator

    required init(coder aDecoder: NSCoder) {
        animator = UIDynamicAnimator()
        super.init(coder: aDecoder)
        // ERROR: Cannot assign to the result of this expression
        animator.referenceView = self.view
    }
}

坑爹啊不是,这个属性居然是只读的!

我们只好把属性设置成 let 的可选类型,并且把初始化放到了 viewDidLoad 里面:

class ViewController: UIViewController {
    private var animator: UIDynamicAnimator?

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        animator = UIDynamicAnimator(referenceView: self.view)
    }
}

这时 animator 终于可以安全的访问 self.view 这个属性了。

你以为这就完了?想太多。

由于是可选类型,所以在使用的时候需要解包:

if let actualAnimator = animator {
  actualAnimator.addBehavior(UIGravityBehavior())
} 

这太丑了,或者用强制解包:

animator!.addBehavior(UIGravityBehavior())

或者用可选链:

animator?.addBehavior(UIGravityBehavior())

都!太!low!了!

这时候不妨试试隐式解析可选类型:

class ViewController: UIViewController {
    private var animator: UIDynamicAnimator!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        animator = UIDynamicAnimator(referenceView: self.view)
    }
}

似乎好看多了。不过这并不是正确的打开方式,我觉得设计可选类型的工程师们应该不希望我们用这个方法,毕竟这更像是为了解决历史遗留包袱所做的妥协。

或许用 lazy 延时加载更合适:

class ViewController: UIViewController {
    lazy private var animator: UIDynamicAnimator = {
        return UIDynamicAnimator(referenceView: self.view)
        }()

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        animator.addBehavior(UIGravityBehavior())
    }
}

References


【电力系统】单机无穷大电力系统短路故障暂态稳定Simulink仿真(带说明文档)内容概要:本文档围绕“单机无穷大电力系统短路故障暂态稳定Simulink仿真”展开,提供了完整的仿真模型与说明文档,重点研究电力系统在发生短路故障后的暂态稳定性问题。通过Simulink搭建单机无穷大系统模型,模拟不同类型的短路故障(如三相短路),分析系统在故障期间及切除后的动态响应,包括发电机转子角度、转速、电压和功率等关键参数的变化,进而评估系统的暂态稳定能力。该仿真有助于理解电力系统稳定性机理,掌握暂态过程分析方法。; 适合人群:电气工程及相关专业的本科生、研究生,以及从事电力系统分析、运行与控制工作的科研人员和工程师。; 使用场景及目标:①学习电力系统暂态稳定的基本概念与分析方法;②掌握利用Simulink进行电力系统建模与仿真的技能;③研究短路故障对系统稳定性的影响及提高稳定性的措施(如故障清除时间优化);④辅助课程设计、毕业设计或科研项目中的系统仿真验证。; 阅读建议:建议结合电力系统稳定性理论知识进行学习,先理解仿真模型各模块的功能与参数设置,再运行仿真并仔细分析输出结果,尝试改变故障类型或系统参数以观察其对稳定性的影响,从而深化对暂态稳定问题的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值