swift学习笔记之-构造过程
//构造过程
import UIKit
/*
构造过程(Initialization):
1.构造过程是使用类、结构体或枚举类型的一个实例的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。通过定义构造器( Initializers )来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法
2.类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察器
构造器:
1.构造器在创建某特定类型的新实例时调用,语法:init(参数) { 在此处执行构造过程 }
2.可以通过输入参数和可选属性类型来自定义构造过程,也可以在构造过程中修改本类的常量属性
3.构造器的参数的功能和语法跟函数和方法的参数相同
4.因为构造器在括号前没有一个可辨别的名字,且可同时定义多个构造器,所以调用时只能通过构造器中的参数名和类型来确定需要调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift会为每个构造器的参数自动生成一个跟内部名字相同的外部名,但是只要构造器定义了某个外部参数名,你就必须使用它
5.可以使用下划线"_"来省略外部名直接通过参数的值直接调用,但该构造器定义时仍然有内部参数名
6.如果你定制的类型包含一个逻辑上允许取值为空的存储型属性--不管是因为它无法在初始化时赋值,还是因为它可以在之后某个时间点可以赋值为空--你都需要将它定义为可选类型optional type 。可选类型的属性将自动初始化为空 nil,无需给他赋初始值
7.你可以在构造过程中的任意时间点修改本类的常量属性的值,只要在构造过程结束时是一个确定的值即可。一旦常量属性被赋值,它将永远不可更改
默认构造器:如果结构体和类的所有属性(包括继承来的属性)都有默认值,同时没有自定义的构造器(无继承构造器),那么 Swift 会给这些结构体和类创建一个默认构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例
结构体的逐一成员构造器:如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。通过给实例的所有属性一一赋值来构造实例
构造器代理(嵌套):构造器可以通过调用其它构造器来完成实例的部分构造过程
1.值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器
2.对于值类型,你可以使用 self.init 在自定义的构造器中引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用 self.init
3.如果你为某个值类型定义了一个定制的构造器,你将无法直接访问到默认构造器,如果是结构体,则无法访问逐一构造器,可以将自己定制的构造器写到扩展( extension )中
类的继承和构造过程:
1.类里面的所有存储型属性--包括所有继承自父类的属性--都必须在构造过程中设置初始值,Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。
2.指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件,默认构造器也是一种指定构造器
3.便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了,便利构造器必须至少调用一个构造器,并至少有一个构造器最终指向一个指定构造器
便利构造器的语法: convenience init(parameters) { statements }
类的构造器代理规则:• 指定构造器必须总是向上代理 • 便利构造器必须总是横向代理
规则 1 指定构造器必须调用其直接父类的的指定构造器。
规则 2 便利构造器必须调用同一类中定义的其它构造器。
规则 3 便利构造器必须最终以调用一个指定构造器结束。
两段式构造过程:Swift 中类的构造过程包含两个阶段
1.第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。
2.当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性
3.两段式构造过程可以防止属性值在初始化之前被访问;也可以防止属性被另外一个构造器意外地赋予不同的值。
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:
安全检查 1 指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。
安全检查 2 指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的 新值将被父类中的构造器所覆盖。
安全检查 3 便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予 的新值将被同一类中其它指定构造器所覆盖。
安全检查 4 构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值, self 的值不能被引用。类实例在第一阶段结束以前并不是完全有效,仅能访问属性和调用方法,一旦完成第一阶段,该实例才会声明为有效实例。
构造流程展示:
阶段 1
• 某个指定构造器或便利构造器被调用;
• 完成新实例内存的分配,但此时内存还没有被初始化;
• 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化;
• 指定构造器将调用父类的构造器,完成父类属性的初始化;
• 这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部;
• 当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完 全初始化。此时阶段1完成。
阶段 2
• 从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问 self 、修改它的属性并调用实例方法等等。
• 最终,任意构造器链中的便利构造器可以有机会定制实例和使用 self 。
构造器的继承和重写:
1.Swift 中的子类不会默认继承父类的构造器。Swift 的这种机制可以防止一个父 类的简单构造器被一个更专业的子类继承,并被错误的用来创建子类的实例
2.重写指定构造器时带上 override 修饰符。即使你重写系统提供的默认构造器也需要带上 override 修饰符,因为默认构造器也是指定构造器。无论是重写属性,方法或者是下标脚本,只要含有 override 修饰符就会去检查父类是否有相匹配的重写指定构造 器和验证重写构造器参数。
3.重写父类的便利构造器时无须带override,因为默认情况下子类不能直接调用父类的便利构造器,除非子类自动继承了父类的构造器
自动继承构造器:默认情况下子类不会继承父类的构造器
如果已经为子类中引入的所有新属性提供了默认值,则:
规则 1 如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
规则 2 如果子类提供了所有父类指定构造器的实现,不管是通过规则1继承过来的,还是通过自定义实现的,它将自动继承所有父类的便利构造器。
可失败构造器(init? int!隐式解包):
1.如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器
2.可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
3.可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过return nil语句来表明可失败构造器在何种情况下应该“失败”,严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功
4.带原始值的枚举类型会自带一个可失败构造器init?(rawValue: )
5.对类而言,可失败构造器只能在类引入的所有存储型属性被初始化后,以及构造器代理调用完成后,才能触发构造失败。
6.类,结构体,枚举的可失败构造器可以横向代理、向上代理其他的可失败或非可失败构造器,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行。
重写一个可失败构造器:
1.如同其它的构造器,你可以在子类中重写父类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个父类的可失败构造器。这使你可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败
2.当你用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包,即在子类的非可失败构造器中使用强制解包来调用父类的可失败构造器
必要构造器:在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器,在子类重写父类的必要构造器时,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加override修饰符。如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现
通过闭包或函数设置属性的默认值:
1.如果某个存储型属性的默认值需要一些定制或设置,你可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性
2.这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值
3.注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性
class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}() //后面有一对(),表示立即执行该闭包
}
*/
struct Celsius {
var temperatureInCelsius: Double = 0.0
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) //调用的是第3个构造器
class SurveyQuestion {
let text: String //let常量属性可以在构造器中被修改(赋值)
var response: String? //可选类型自动初始化为nil,无需为其赋值
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
//===========构造器代理(嵌套)=======
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) //代理(嵌套)构造器
}
}
//==========构造器重写=========
class Vehicle {
var numberOfWheels = 0
let a = 1
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
let b:Int
override init() { //重写父类的默认构造器
self.b = 3
super.init() //调用父类的默认构造器
numberOfWheels = 2 //调用完父类的构造器后,再给从父类继承来的属性修改值,防止被覆盖
//a = 2 //不能在构造器中修改从父类继承来的常量属性的值,因为在父类构造器中已经给常量属性赋过值
}
init(x:Int){
self.b = x //给本类的常量赋值
super.init()
numberOfWheels = 5
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// Bicycle: 2 wheel(s)
//======展示指定构造器、便利构造器和自动构造器的继承======
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() { //便利构造器,它必须至少调用一个构造器,并至少有一个构造器最终指向一个指定构造器
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
let mysteryMeat = Food()
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)
class ShoppingListItem: RecipeIngredient { //本类为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器, ShoppingListItem 将自动继承所有父类中的指定构造器和便利构造器。
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 ?
//============可失败构造器======
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let someCreature = Animal(species: "Giraffe") // someCreature 的类型是 Animal? 而不是 Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
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.")
}
//=========带默认值的枚举类型会自带一个可失败构造器init?(rawValue:)=====
enum TemperatureUnit1: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnit1 = TemperatureUnit1(rawValue: "F")
if fahrenheitUnit1 != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
//========类的可失败构造器,可失败构造器的代理==========
class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
if let bowTie = Product(name: "bt") {
// 不需要检查 bowTie.name 是否为 nil
print("The product's name is \(bowTie.name)")
}
class CartItem: Product {
let quantity: Int!
init?(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name) //代理了父类的可失败构造器,若父类构造失败,则整个构造失败,接下来的任何构造代码不会再被执行。
if quantity < 1 { return nil }
}
}
//========重写可失败构造器======
class Document {
var name: String?
init() {}
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
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]")! //对父类的可失败构造器强制解包
}
}
//==========通过闭包或函数设置属性的默认值======
struct Checkerboard {
let boardColors: [Bool] = { //闭包定义开始
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...10 {
for j in 1...10 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard //返回临时变量的值给属性
}() //闭包定义结束
func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column]
}
}
let board = Checkerboard()
print(board.squareIsBlackAtRow(0, column: 1))
// 打印 "true"
print(board.squareIsBlackAtRow(9, column: 9))
// 打印 "false"
swift学习笔记之-构造过程的更多相关文章
- swift学习笔记(五)构造过程
构造过程是为了使用某个类.结构体或枚举类型的实例而进行的准备过程.在构造过程中,对每一个属性进行了初始值预设和其它必要的准备和初始化工作. 与OC相比,swift的构造函数.不须要返回值.同一时候,在 ...
- swift学习笔记之-析构过程
//析构过程deist import UIKit /*析构过程(Deinitialization):析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用.析构器用关键字deinit来标示 ...
- swift学习笔记3——类、结构体、枚举
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- 【swift学习笔记】二.页面转跳数据回传
上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...
- Swift学习笔记(一)搭配环境以及代码运行成功
原文:Swift学习笔记(一)搭配环境以及代码运行成功 1.Swift是啥? 百度去!度娘告诉你它是苹果最新推出的编程语言,比c,c++,objc要高效简单.能够开发ios,mac相关的app哦!是苹 ...
- Swift 学习笔记 (二)
原创:转载请注明出处 41.闭包表达式语法(Closure Expression Syntax) 闭包表达式语法有如下一般形式: { (parameters) -> returnType in ...
- swift学习笔记1——基础部分
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- Swift 学习笔记 (三) 之循环引用浅析
原创:转载请注明出处 110.自动引用计数实践 下面的例子展示了自动引用计数的工作机制.例子以一个简单的Person类开始,并定义了一个叫name的常量属性: class Person { l ...
- swift学习笔记5——其它部分(自动引用计数、错误处理、泛型...)
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
随机推荐
- 解决clone问题之外的体会
adlnkoh.sh started at Thu Aug 25 15:42:51 CST 2016 Log file located at /u02/db/testdb/11.1.0/appsuti ...
- sitemesh2在tomcat和weblogic中同时使用的配置问题
(一)拦截*.do,装饰器中匹配do tomcat 可行 weblogic 不可行 web.xml ~~~ <filter> <filter-name>sitemesh< ...
- [LeetCode] Longest Valid Parentheses 动态规划
Given a string containing just the characters '(' and ')', find the length of the longest valid (wel ...
- js获取客户端计算机硬件及系统信息
注意:(1):遇到“automation服务器不能创建对象”的问题 解决方案: 1.在“运行”中执行regsvr32 scrrun.dll 2.安全模式设置成“中”,如果javascript脚本中报这 ...
- VB6.0手册
1.Form窗体事件 Private Sub Form_Activate() '焦点在此窗口时触发 MsgBox "窗体的Activate事件" End Sub Priv ...
- [Ubuntu] bash: warning: setlocale: LC_ALL: cannot change locale
问题症状 -bash: warning: setlocale: LC_ALL: cannot change locale (en_US.utf8) 解决方法 本地化是指不同地区用户在键盘上输入不同语言 ...
- 链表的实现(Java语言描述)
代码如下: public interface ListInterface<T> { public T getElem(int i); public boolean insertElem(i ...
- sed用例
文件空行处理 1. 在文件中的每一行后面添加一个空行. sed 'G' test.txt 解释: Get命令是将保留空间的内容取出,并添加到当前模式空间的内容之后(添加一行).当保留空间为空时,效果为 ...
- IE11之F12 Developer Tools--概述篇
打开Developer Tools的方法: a. 点击F12 b. 在浏览器中选择Tools-->F12 Develooper Tools 打开后图示: 从上图我们可以看到,Developer ...
- 使用Eclipse Installer安装Eclipse
由于一些原因,需要重新安装Eclipse,登陆到Eclipse官网下载Eclipse时发现社区又推出了Eclipse Installer.所以就下下来尝尝鲜. 刚开始确实有些选项不太明白,不过现在挺喜 ...