文档章节

Swift11/90Days - 蛋疼的初始化过程

hejunbinlan
 hejunbinlan
发布于 2015/06/24 12:18
字数 1025
阅读 420
收藏 0
点赞 0
评论 0

蛋疼的初始化过程

阶段构造

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

本文转载自:http://segmentfault.com/a/1190000002392828

共有 人打赏支持
hejunbinlan
粉丝 40
博文 534
码字总数 21018
作品 0
浦东
高级程序员
奇淫怪巧之给Delphi的PrintDialog增加一个页码选定范围打印的Edit

在Delphi中使用PrintDialog打印对话框的时候,这个控件有三个选项,就是PrintRang那个属性的三个选项,其中有一个选项三,让我们自定义选择页码范围来打印。但是比较蛋疼的是,这个地方选中了...

技术小美 ⋅ 2017/11/12 ⋅ 0

swfit UITabBar UITabBarItem设置图标和文字颜色

搞了一下,蛋疼,辛苦@岁月恍惚 ,谢谢。下面记录下过程。 我在stroyboard里面已经设置了默认图标和选中图标,但没有任何效果,为何放出这个设置呢,没懂。 设置不行,只有通过代码进行控制了...

jack_peng ⋅ 2016/07/28 ⋅ 0

如何加快django单元测试的速度?

django单元测试的时候会建立数据库,即使是只测一个模块,当表以及索引多到一定程度的时候建表的速度真心蛋疼,有没有办法加快这个速度? 1. 有没有办法只建立一次数据库,或者在配置文件里面...

duoduo3_69 ⋅ 2014/03/13 ⋅ 1

Ubuntu 登录闪退

大清早来上班就碰到了那么但疼的情况,然后直接修改 /bin/pyenv的权限 还是不行;再ctrl+alt+f1 进入到命令行界面查看一下错误日志 居然报错 X11 什么的 @im尼玛之前用的是fcitx 原生的 昨天手...

纯洁徐 ⋅ 2016/04/20 ⋅ 0

让人蛋疼的Objective-C语法

真的很蛋疼,发明这个语言的家伙肯定是因为自己的蛋疼了才发明了这个语言,然后推广这个语言的人因为蛋疼也就推了这个语言,最后蛋疼的受害者就是开发者,包括我这样的爱好者。 最蛋疼的语法...

kut ⋅ 2012/01/16 ⋅ 4

边运行junit,边调试,蛋疼

这段时间无聊。开始用TDD模式开发android app。嘿,发现还真爽。一整天不碰device,一直就看着屏幕上出现“OK”或者“Fail"的感觉还不错。 由于本人是IDE去死团团员, 所以运行junit的方式就...

ufo.22940268 ⋅ 2012/06/08 ⋅ 2

求助软件测试员需要具备的各种条件?各种各种。。!

决定了,软件测试员。 各位大神说的需要的各种条件我都考虑! 会什么编程语言或者什么的。 多多益善,全能下好了,虽然过程会蛋疼菊花紧…

HeiCk ⋅ 2012/11/08 ⋅ 1

dbutils的奇葩问题

最近在开一个项目,有一个需求是:在项目启动时,启动一个线程,这个线程不断的查询数据库。 数据库查询这块用的是apache dbutils,线程是在一个ServletContextListener的init方法中启动的。...

tianpeng91 ⋅ 2014/06/20 ⋅ 5

ms-duplex怎么在选option的时候获取值?

我从数据库读取出来json数组以后,用select进行显示,再用duplex进行双向绑定,在选取选项的时候获取它的value值(值跟选项的内容不一样,相当于一个是用户名,一个用户ID)。为什么导入数据...

念通幽 ⋅ 2016/07/25 ⋅ 0

spark内核揭秘-05-SparkContext核心源码解析初体验

SparkContext在获得了一系列的初始化信息后开始创建并启动TaskScheduler实例: 进入createTaskScheduler方法: 我们看一下其Standalone的方式: 在上述代码中首先实例化一个TaskSchedulerIm...

stark_summer ⋅ 2015/01/19 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

vuex学习

1、getters基本用法: 在store.js里面用const声明我们的getters属性。 const getters={ count:function (state) { return state.count +=100; }} export default new Vuex.S......

大美琴 ⋅ 37分钟前 ⋅ 0

292. Nim Game - LeetCode

Question 292. Nim Game Solution 思路:试着列举一下,就能发现一个n只要不是4的倍数,就能赢。 n 是否能赢1 true2 true3 true4 false 不论删除几,对方都能一把赢5 t...

yysue ⋅ 49分钟前 ⋅ 0

G6 关系数据可视化图形库 简单使用

官网 https://antv.alipay.com/zh-cn/g6/1.x/index.html 效果 首先生成给定数目的小球,并设置随机的颜色 按照顺序,设置小球的角度以及坐标 设置定时器,每隔一定的时间修改小球的角度和坐标...

阿豪boy ⋅ 51分钟前 ⋅ 0

6.5 zip压缩工具 6.6 tar打包 6.7 打包并压缩

zip压缩工具 zip命令可以压缩目录和文件,-r 压缩目录。 zip使用方法 zip 1.txt.zip 1.txt //压缩文件 zip -r 123.zip 123/ //压缩目录 unzip 1.txt.zip //解压 unzip 123.zip -d /root/456...

Linux_老吴 ⋅ 今天 ⋅ 0

react-loadable使用跳坑

官方给react-loadable的定义是: A higher order component for loading components with dynamic imports. 动态路由示例 withLoadable.js import React from 'react'import Loadable fro......

pengqinmm ⋅ 今天 ⋅ 0

记录工作中遇到的坑

1、ios safari浏览器向下滚动会触发window resize事件

端木遗风 ⋅ 今天 ⋅ 0

桥接设计模式

1、概述: 将抽象部分与他的实现部分分离,这样抽象化与实现化解耦,使他们可以独立的变化 如何实现解耦的呢,就是通过提供抽象化和实现化之间的桥接结构 桥接模式将继承模式转化成关联关系,他降...

职业搬砖20年 ⋅ 今天 ⋅ 0

20.zip压缩 tar打包 打包并压缩

6月25日任务 6.5 zip压缩工具 6.6 tar打包 6.7 打包并压缩 6.5 zip压缩工具: zip支持压缩目录 zip压缩完之后原来的文件不删除 不同的文件内容其实压缩的效果不一样 文件内有很多重复的用xz压...

王鑫linux ⋅ 今天 ⋅ 0

double类型数据保留四位小数的另一种思路

来源:透析公式处理,有时候数据有很长的小数位,有的时候由在四位以内,如果用一般的处理方法,那么不足四位的小树会补充0到第四位,这样子有点画蛇添足的感觉,不太好看。所以要根据小数的...

young_chen ⋅ 今天 ⋅ 0

Django配置163邮箱出现 authentication failed(535)错误解决方法

最近用Django写某网站,当配置163邮箱设置完成后,出现535错误即:smtplib.SMTPAuthenticationError: (535, b'Error: authentication failed') Django初始配置邮箱设置 EMAIL_HOST = "smtp.1...

陈墨轩_CJX ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部