Swift中的init方法
摘要:Swift有着超级严格的初始化方法,不仅强化了designated初始化方法的地位,所有不加修饰的init方法都需要在方法中确保非Optional的实例变量被赋值初始化,而在子类中,也强制调用super版本的designated初始化。
我们在深入初始化方法之前,不妨先再想想Swift中的初始化想要达到一种怎样的目的。
其实就是安全。在Objective-C中,init方法是非常不安全的:没有人能保证init只被调用一次,也没有人保证在初始化方法调用以后,实例的各个变量都完成初始化,甚至如果在初始化里使用属性进行设置的话,还可能会造成各种问题。虽然Apple也明确说明了不应该在init中使用属性来访问,但这并不是编译器强制的,因此还是会有很多开发者犯这样的错误。
所以Swift有了超级严格的初始化方法。一方面,Swift强化了designated初始化方法的地位。Swift中不加修饰的init方法都需要在方法中保证所有非Optional的实例变量被赋值初始化,而在子类中也强制 (显式或隐式地)调用super版本的designated初始化,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。
- class ClassA {
- let numA: Int
- init(num: Int) {
- numA = num
- }
- }
- class ClassB: ClassA {
- let numB: Int
- override init(num: Int) {
- numB = num + 1
- super.init(num: num)
- }
- }
在上面的示例代码中,注意在init里我们可以对let的实例常量进行赋值,这是初始化方法的重要特点。在Swift中let声明的值是不变量,无法被写入赋值,这对于构建线程安全的API十分有用。而因为Swift的init只可能被调用一次,因此在init中我们可以为不变量进行赋值,而不会引起任何线程安全的问题。
与designated初始化方法对应的是在init前加上convenience关键字的初始化方法。这类方法是Swift初始化方法中的“二等公民”,只作为补充和提供使用上的方便。所有的convenience初始化方法都必须调用同一个类中的designated初始化完成设置,另外convenience的初始化方法是不能被子类重写或从子类中以super的方式被调用的。
- class ClassA {
- let numA: Int
- init(num: Int) {
- numA = num
- }
- convenience init(bigNum: Bool) {
- self.init(num: bigNum ? 10000 : 1)
- }
- }
- class ClassB: ClassA {
- let numB: Int
- override init(num: Int) {
- numB = num + 1
- super.init(num: num)
- }
- }
只要在子类中实现重写了父类convenience方法所需要的init方法的话,我们在子类中就也可以使用父类的convenience初始化方法了。比如在上面的代码中,我们在ClassB里实现了init(num: Int)的重写。这样,即使在ClassB中没有bigNum版本的convenience init(bigNum: Bool),我们仍然还是可以用这个方法来完成子类初始化:
- let anObj = ClassB(bigNum: true)
- // anObj.numA = 10000, anObj.numB = 10001
因此进行一下总结,可以看到初始化方法永远遵循以下两个原则:
- 初始化路径必须保证对象完全初始化,这可以通过调用本类型的designated初始化方法来得到保证;
- 子类的designated初始化方法必须调用父类的designated方法,以保证父类也完成初始化。
对于某些我们希望子类中一定实现的designated初始化方法,我们可以通过添加required关键字进行限制,强制子类对这个方法重写实现。这样的一个最大的好处是可以保证依赖于某个designated初始化方法的convenience一直可以被使用。一个现成的例子就是上面的init(bigNum: Bool):如果我们希望这个初始化方法对于子类一定可用,那么应当将init(num: Int)声明为必须,这样我们在子类中调用init(bigNum: Bool)时就始终能够找到一条完全初始化的路径了:
- class ClassA {
- let numA: Int
- required init(num: Int) {
- numA = num
- }
- convenience init(bigNum: Bool) {
- self.init(num: bigNum ? 10000 : 1)
- }
- }
- class ClassB: ClassA {
- let numB: Int
- required init(num: Int) {
- numB = num + 1
- super.init(num: num)
- }
- }
另外需要说明的是,其实不仅仅是对designated初始化方法,对于convenience的初始化方法,我们也可以加上required以确保子类对其进行实现。这在要求子类不直接使用父类中的convenience初始化方法时会非常有帮助。
Swift中的init方法的更多相关文章
- Swift基础--Swift中的分类以及在分类中扩展init方法的注意事项
Swift中的分类 1.创建一个空的swift文件 2.关键字extension,格式: extension 要扩展的类名 {} extension UIButton { } Swift中扩展init ...
- IOS开发中重写init方法使用需谨慎
IOS开发中重写init方法使用需谨慎 今天在写一个小软件的时候出现一点问题,这个软件的功能是搜索全国学校,首页就是搜索输入框,在框中输入完要查询的学校所在省份,点击buttom后就会跳转到对应的视图 ...
- iOS开发之swift与OC混编出现的坑,oc中不能对swift的代理进行调用,不能访问swift中的代理,swift中的回调方法
1. Swift与oc混编译具体怎么实现,这儿我就不重复讲出了,网上有大把的人讲解. 2. 在swift与OC混编的编译环境下, oc类不能访问swift创建类中的代理? 解决方法如下: 在代理的头部 ...
- Swift基础之init方法,实例方法,类方法(静态方法)的使用(多标签Demo)
Xcode更新过后,有些方法都进行了改变,Demo中有变化的都进行了简单的标记,具体以后遇见再说 创建一个UIView类,用init方法创建两种类型,显示多标签,创建静态方法进行调用,创建类方法进行调 ...
- OC中自定义init方法
---恢复内容开始--- 我们知道,在函数中实例化一个对象,大多数会同时进行初始化,如 Person *p =[ [Person alloc]init]; 此时已经进行了初始化,使用init方法,那么 ...
- Swift新手教程系列5-函数+selector在swift中的使用方法
原创blog.转载请注明出处 近期在用swift写代码,尽管遇到一些问题,可是代码量确实减了不少. swfit新手教程系列会随着我使用swfit中的积累,不断地去修正更新 之前的教程 swift单例模 ...
- Swift中自定义打印方法
// 1.获取打印所在的文件 let file = ( #file as NSString).lastPathComponent // 2.获取打印所在的方法 let funcName = #func ...
- python中的Init方法, new 方法 call 方法
new 方法实现单列模式思考 class Single: _single = None _single_only = None def __init__(self, value): self.v = ...
- python 子类中定义init方法
随机推荐
- node+pjax实现不刷新跳转
做前端的都知道如果通过a标签去访问跳转到某一个页面,浏览器会自动刷新.那么如何实现不刷新跳转? html5的出现让我们可以实现不刷新跳转页面.主要使用的方法:history.pushState(dat ...
- python 基本类型的创建方法
1.int class int(object) | int(x=0) -> integer | int(x, base=10) -> integer | | Convert a numbe ...
- bzoj 3513: [MUTC2013]idiots【生成函数+FFT】
想了好长时间最后发现真是石乐志 第一反应就是两边之和大于第三边,但是这个东西必须要满足三次-- 任意的两边之和合通过生成函数套路+FFT求出来(记得去掉重复选取的),然后这任意两边之和大于任意第三边可 ...
- scanf()和scanf_s()
在最初的C语言中,原版的输入就是scanf("<格式化字符串>",<地址表>) ANSI C中没有scanf_s(),只有scanf(),scanf()在读 ...
- python中时间日期格式化符号的含义
%y 两位数的年份表示(00-99) %Y 四位数的年份表示(000-9999) %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数(0-23) %I 12 ...
- flask跨域问题
在Flask开发RESTful后端时,前端请求会遇到跨域的问题.下面是解决方法: 使用 flask-cors库可以很容易的解决 1 pip install flask-cors 两种方法,一个是全 ...
- A.华华听月月唱歌
链接:https://ac.nowcoder.com/acm/contest/392/A 题意: 月月唱歌超级好听的说!华华听说月月在某个网站发布了自己唱的歌曲,于是把完整的歌曲下载到了U盘里.然而华 ...
- 设置Linux环境变量的方法和区别_Ubuntu/CentOS
设置 Linux 环境变量可以通过 export 实现,也可以通过修改几个文件来实现,有必要弄清楚这两种方法以及这几个文件的区别. 通过文件设置 Linux 环境变量 首先是设置全局环境变量,对所有用 ...
- 题解报告:hdu 4607 Park Visit(最长链)
Problem Description Claire and her little friend, ykwd, are travelling in Shevchenko's Park! The par ...
- linux系统文件目录解析
/bin 二进制可执行命令 /dev 设备文件(硬盘/光驱等) /etc 系统管理和配置文件 /etc/rc.d 启动的配置文件和脚本 /home 用户主目录,下面会有以登录用户名作为文件夹名 ...