Swift从入门到精通第十一篇 - 初始化 初识
初始化(学习笔记)
环境Xcode 11.0 beta4 swift 5.1
初始化
- 初始化是类、结构体、枚举生成实例的过程,为该类的每个存储属性设置初始值,有些在实例使用前的设置或初始化也可在此实现;
- Swift初始化函数不用写返回值,确保新类型的实例在使用前被正确初始化
- 类类型也可以实现反初始化器,可以在实例销毁的时自定义清理操作
为存储属性设置初始值
- 类和结构体必须为所有存储属性设置一个合适的值
- 可以在初始化或属性定义的时候设置值,此时设置的值不会触发属性观察器
- 初始化器:创建一个指定类型的实例,用关键字
initinit() {
// perform some initialization here
}
// 定义一个结构体类
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"
- 默认属性初始值,属性声明时赋值
struct Fahrenheit {
var temperature = 32.0
}
自定义初始化
- 初始化参数:可以在初始化定义时提供初始化参数,与函数和方法参数的功能一样
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
- 参数名字和参数标签:参数名字函数体内使用,参数标签用在方法调用时使用
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - argument labels are required
- 不带参数标签的初始化器,用
_代替struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
- 可选类型属性会自动初始化一个
nil值,就算调用初始化器以后也有可能是没有值class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
- 初始化过程中给常量属性赋值,与在定义常量时赋值效果一样且只能赋值一次,子类也不能修改
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text // 仅只能赋值一次
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// Prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
- 初始化参数:可以在初始化定义时提供初始化参数,与函数和方法参数的功能一样
默认初始化器
- 类和结构体有一个默认的初始化器,为所有存储属性设置默认值
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem() // 为三个属性设置默认值, 可选类型 nil 也算
- 结构体类型成员初始化器,可以自动根据存储属性是在定义时有值生成多个初始化器
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
let zeroByTwo = Size(height: 2.0)
print(zeroByTwo.width, zeroByTwo.height)
// Prints "0.0 2.0"
//
let zeroByZero = Size()
print(zeroByZero.width, zeroByZero.height)
// Prints "0.0 0.0"
- 类和结构体有一个默认的初始化器,为所有存储属性设置默认值
值类型初始化器代理
- 初始化器可以调用其它初始化器,俗称初始化器代理;值类型和
class类型不同,值类型(结构体、枚举)不支持继承,简单调用就好;class类型要考虑所有的存储属性(包括继承的)要赋值一个合适的值;对于值类型用self.init可以调用其它初始化器;如果是值类型自定义初始化器则系统不会自动生成初始化器;如果想自定义不影响自动生成的初始化器,可以将自定义的写在extension中struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
let basicRect = Rect()
// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
- 初始化器可以调用其它初始化器,俗称初始化器代理;值类型和
类的继承和初始化
- 类的所有存储属性在初始化时都必须要有初始值, swift提供了两种初始化器:初始化器和便捷初始化器;每个类要有一个指定的初始化器,通常一个类只有一个
- 指定初始化器和便捷初始化器语法
init(#parameters#){
#statements#
}
convenience init(#parameters#){
#statements#
}
class类型初始化器代理,指定和便捷初始化器遵循以下三条准则:1. 指定初始化器必须调用直接父类的一个指定初始化器;2. 便捷初始化器必须调用同类的一个其它初始化器;3. 便捷初始化器最终要调用一个指定初始化器;见下图

- 初始化两阶段:第一阶段:每一个存储属性赋一个初始值;第二阶段:可以进一步自定义存储属性相关操作;Swift编译器通过4个安全检查来确保两阶段初始化的完整性
- 安全检查一、指定的初始化器必须确保在将其委托给父类初始化器之前初始化其类引入的所有属性;只有在知道对象所有存储属性的初始状态之后,才会认为对象的内存已经完全初始化。为了满足这个规则,指定的初始化器必须确保在传递到链之前初始化了它自己的所有属性。
- 安全检查二、指定初始化器给继承属性赋值前必须先要委托到父类初始化器,如果不这样做赋的值会被父类覆盖
- 安全检查三、便捷初始化器给任意一个属性赋值前必须要委托到一个初始化器,如果不这样做赋的值会被自己类的指定初始化器覆盖
- 安全检查四、在初始化第一阶段完成前,初始化器不能调用任何实例方法、读取任何实例属性的值或者引用
self做为一个值
- 基于以上四项检查,以下是两阶段执行过程
- 阶段一
- 一个指定或便捷初始化器被调用
- 分配类的新实例的内存,内存还没有初始化完成
- 指定初始化器确保类的所有存储属性有一个值,这些存储属性内存初始化完成
- 指定初始化器切换到父类的初始化器为它的存储属性执行同样操作
- 上一个步骤会顺着继承链向上传递直到基类
- 一旦到达基类且所有存储属性都有值,实例的内存才被认为完整初始化,阶段一完成
- 阶段一

- 阶段二
+ 从基类向下每个指定初始化器完成自己实例的自定义操作;初始化器此时可以访问 `self` 、修改自己的属性、调用实例方法等等等
+ 最后,在继承链中任一便捷初始化器有自定义实例和使用 `self` 选项

初始化器的继承和重写
Swift默认不继承父类初始化器,在某些特定的情况下(安全和合适)才会被继承- 如果子类想自定义一个或多个父类的初始化器,可以提供自定义的实现
- 如果在重写父类的指定初始化器,要加修饰符
override - 根据上面的两个阶段规则,子类在重写父类的便捷初始化器不需要
override修饰,因为不会直接调用父类的便捷初始化器
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
// Vehicle 没有提供自定义的初始化器,会有一个默认的初始化器且是指定初始化器
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
// Vehicle: 0 wheel(s)
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// Bicycle: 2 wheel(s)
- 如果子类初始化器在第二阶段没有执行自定义操作且父类有一个无参的指定初始化器,则子类可以在为所有存储属性赋值后省略
super.init()调用
class Hoverboard: Vehicle {
var color: String
init(color: String) {
self.color = color
// super.init() implicitly called here
}
override var description: String {
return "\(super.description) in a beautiful \(color)"
}
}
let hoverboard = Hoverboard(color: "silver")
print("Hoverboard: \(hoverboard.description)")
// Hoverboard: 0 wheel(s) in a beautiful silver
自动初始化器继承
- 假定子类为每一个属性设置一个默认值,那只需遵循以下两条规则:
- 规则一:如果子类没有定义任何指定初始化器,将会自动继承父类的所有指定初始化器
- 规则二:如果子类提供父类的所有指定初始化器的实现(要么按照规则一继承,要么作为定义的一部分提供自定义实现)它将继承父类所有便捷初始化器
- 即使子类添加了更便捷的初始化器,以上规则仍然适用
- 根据上面规则二可推导出:子类可以实现父类的指定初始化器作为自己的便捷初始化器
指定和便捷初始化器的应用
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
// 根据上面规则 RecipeIngredient 实现了父类的所有初始化器,因此要继承父类所有便捷初始化器
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
// ShoppingListItem没有定义初始化器,因此自动继承父类的所有指定和便捷初始化器
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘
// 以上继承关系图如下

可失败的初始化器
可失败初始化器是在定义方式为:
init?()struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
//
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
//
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"
枚举可失败初始化器的定义
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
//
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
带有原始值枚举的可失败初始化器
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
//
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
//
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
初始化失败传递
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
//
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// Prints "Item: sock, quantity: 2"
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// Prints "Unable to initialize zero shirts"
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// Prints "Unable to initialize one unnamed product"
可失败初始化器重写
- 像其它初始化器一样,可以重写父类保失败初始化器且可以重写为不可失败的初始化器,此时要强制解包可失败初始化器的结果
- 不能将不可失败初始化器重写为可失败初始化器
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")! // 此处强制解包
}
}
可失败初始化隐式解包 (init!)
- 定义一个隐式解包可失败初始化器,用
init!,可以用init!重写init?反之亦可,可以委托init?给init!, 反之亦可;当从init委托给init!,如果初始化失败会触断言
- 定义一个隐式解包可失败初始化器,用
必须的初始化器
- 用
required修饰符的初始化器,子类必须要实现,且重写时不需要override修饰class SomeClass {
required init() {
// initializer implementation goes here
}
}
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
- 用
用闭包或函数为属性设置默认值
- 用闭包设置默认值,闭包执行在实例的属性初始化之前,所以闭包内不能显示调用
self、属性(包括有默认值)、实例方法,模板大概如下class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
// 闭包简单应用实例:
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"
- 用闭包设置默认值,闭包执行在实例的属性初始化之前,所以闭包内不能显示调用
Swift从入门到精通第十一篇 - 初始化 初识的更多相关文章
- Swift从入门到精通第八篇 - 方法 初识
方法(学习笔记) 环境Xcode 11.0 beta4 swift 5.1 方法 结构体.枚举.类都可以定义方法(实例方法.类型方法) 实例方法(Instance Methods) 实例方法只能用实例 ...
- Swift从入门到精通第七篇 - 扩展 初识
扩展(学习笔记) 环境Xcode 11.0 beta4 swift 5.1 扩展 为类.结构体.枚举.协议添加新功能,同OC的分类很像,但扩展没有名字 扩展可以添加计算实例属性和计算类型属性(不能添加 ...
- Redis从入门到精通:初级篇
原文链接:http://www.cnblogs.com/xrq730/p/8890896.html,转载请注明出处,谢谢 Redis从入门到精通:初级篇 平时陆陆续续看了不少Redis的文章了,工作中 ...
- Redis从入门到精通:初级篇(转)
原文链接:http://www.cnblogs.com/xrq730/p/8890896.html,转载请注明出处,谢谢 Redis从入门到精通:初级篇 平时陆陆续续看了不少Redis的文章了,工作中 ...
- SaltStack 入门到精通第三篇:Salt-Minion配置文件详解
SaltStack 入门到精通第三篇:Salt-Minion配置文件详解 作者:ArlenJ 发布日期:2014-06-09 17:52:16 ##### 主要配置设置 ##### 配置 默认值 ...
- Spring Boot从入门到精通(十一)集成Swagger框架,实现自动生成接口文档
Swagger是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.Swagger 是一组开源项目,其中主要要项目如下: Swagger-tools:提供各种与S ...
- Hibernate从入门到精通(十一)多对多双向关联映射
上次我们在中Hibernate从入门到精通(十)多对多单向关联映射讲解了一下多对多单向关联映射,这次我们讲解一下七种映射中的最后一种多对多双向关联映射. 多对多双向关联映射 按照我们之前的惯例,先看一 ...
- [置顶] Hibernate从入门到精通(十一)多对多双向关联映射
上次我们在中Hibernate从入门到精通(十)多对多单向关联映射讲解了一下多对多单向关联映射,这次我们讲解一下七种映射中的最后一种多对多双向关联映射. 多对多双向关联映射 按照我们之前的惯例,先看一 ...
- Egret入门学习日记 --- 第十一篇(书中 4.1~4.6节 内容)
第十一篇(书中 4.1~4.6节 内容) 好了,到了这篇开始,前三章都记录完了. 接下来就是到第四章了. 4.1节 的内容总结一下重点: 1.resource目录下default.res.json文件 ...
随机推荐
- 集成方法 Ensemble
一.bagging 用于基础模型复杂.容易过拟合的情况,用来减小 variance(比如决策树).基础模型之间没有太多联系(相对于boosting),训练可以并行.但用 bagging 并不能有助于把 ...
- react-native-gesture-handler报错
安装React Native第三方组件出现Task :react-native-gesture-handler:compileDebugJavaWithJavac FAILED报错,则使用jetifi ...
- 同“窗”的较量:部署在 Windows 上的 .NET Core 版博客站点发布上线
为了验证 docker swarm 在高并发下的性能问题,周一我们发布了使用 docker-compose 部署的 .net core 版博客站点(博文链接),但由于有1行代码请求后端 web api ...
- 送礼物「JSOI 2015」RMQ+01分数规划
[题目描述] 礼品店一共有N件礼物排成一列,每件礼物都有它的美观度.排在第\(i(1\leq i\leq N)\)个位置的礼物美观度为正整数\(A_I\).JYY决定选出其中连续的一段,即编号为礼物\ ...
- Linux配置及指令
目录 Linux配置及指令 一.linux中常用软件的安装 二.主机名和网络 1.修改主机名 2.设置网络 三.关闭防火墙 1.检查防火墙是否开启 2.清除策略 3.永久关闭第一个防火墙 4.关闭第二 ...
- centos7.x 安装系统/配置网络/设置主机名
1.安装系统 系统的安装就不多说了,自行查找百度,如:https://www.cnblogs.com/wcwen1990/p/7630545.html 2.配置网络(局域网上网) 修改配置 ...
- 帝国CMS(EmpireCMS) v7.5 后台XSS漏洞分析
帝国CMS(EmpireCMS) v7.5 后台XSS漏洞分析 一.漏洞描述 该漏洞是由于代码只使用htmlspecialchars进行实体编码过滤,而且参数用的是ENT_QUOTES(编码双引号和单 ...
- Java基础及JavaWEB以及SSM框架学习笔记Xmind版
Java基础及JavaWEB以及SSM框架学习笔记Xmind版 转行做程序员也1年多了,最近开始整理以前学习过程中记录的笔记,以及一些容易犯错的内容.现在分享给网友们.笔记共三部分. JavaSE 目 ...
- JavaScript数组方法大全(第一篇)
数组方法大全(第一篇) 注意:第一次写博客有点小紧张,如有错误欢迎指出,如有雷同纯属巧合,本次总结参考书籍JavaScript权威指南,有兴趣的小伙伴可以去翻阅一下哦 join()方法 该方法是将数组 ...
- 关于Function和Object之间先后问题的相关研究
文章说明,博主是一个前端小白,本片文章是博主在学习的过程中碰到的疑惑,根据查找的资料,之后得出的个人结论,文中如果出现错误,欢迎指正. -------路漫漫其修远兮吾将上下而求索,与诸君共勉----- ...