//构造过程: 是使用类、结构体或枚举类型的实例之前的准备过程。具体包括:设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。
//类的实例也可以通过定义析构器在实例释放之前执行特定的清除工作。
//=================================== 存储属性的初始赋值 ===================================
//类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。可以在构造器中赋初始值,也可以在定义属性时为其设置默认值,赋值时不会触发任何属性观察者,是直接设置的。
//构造器:在创建某个特定类型的新实例时被调用,以关键字init命名:
struct Fahrenheit {//华氏温度
var temperature: Double //温度
init() {//不带参数的构造器,在此处执行构造过程
temperature = 32.0
}
}
var f = Fahrenheit()
print("构造器设置的初始温度是:\(f.temperature)华氏度")
//属性默认值:在构造器中为存储型属性赋值、在属性声明时赋值
struct Fathrennheit2{
var temperature2 = 32.0 //在属性声明时赋值,比在构造器中设置初始值更简洁、清晰
}
var f2 = Fathrennheit2()
print("属性声明设置的初始温度是:\(f2.temperature2)华氏度")
//=================================== 自定义构造过程 ======================================
//可以通过输入参数和可选类型的属性来自定义构造过程,也可以在构造过程中修改常量属性。
//构造参数、参数的内部名称和外部名称、不带外部名的构造器参数
struct Celsius{//摄氏度
var temperatureInCelsius: Double //摄氏温度
init(fromFahrenheit fahrenheit: Double) { //构造方法1:华氏温标
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromeKelvin kelvin: Double) { //构造方法2:开氏温标
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {//构造方法3:摄氏度;外部名用”_“表示没有外部名称
temperatureInCelsius = celsius
}
//以第一个构造器为例:外部参数名为fromFahrenheit,内部参数名为fahrenheit,将其参数值转换成摄氏温度值,并保存在属性temperatureInCelsius中
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) //水沸点:华氏 212°
let freezingPointOfWater = Celsius(fromeKelvin: 273.15) // 水的冰点:开氏 273.15°
let todayTemp = Celsius(35.0)//不需要外部名,可以通过提供的Double类型的参数值来调用构造器
print("水的沸点是:\(boilingPointOfWater.temperatureInCelsius)℃ 水的冰点是:\(freezingPointOfWater.temperatureInCelsius)℃ 今天的温度是:\(todayTemp.temperatureInCelsius)℃ ") //水的沸点是:100.0℃ 水的冰点是:0.0℃ 今天的温度是:35.0℃
//可选属性类型: 自定义类型中包含一个允许取值为空的存储型属性(未赋初始值或赋值为空),需要将其定义为可选类型,可选类型的属性将自动初始化为nil。
class SurveyQuestion {
var text: String
var response: String? //可选字符串属性,用于回答问题;但问题的答案在初始化时是无法确定的,所以声明为可选类型
init(text: String) {//提问
self.text = text
}
func ask(){//打印问题
print("问:\(text)")
}
func answer(){
print("答:\(response ?? "没有回答")")
}
}
let cheeseQuestion = SurveyQuestion(text: "今天天气怎么样?")
cheeseQuestion.ask()
cheeseQuestion.response = "今天天气炎热,有雾霾,夜间可能会有雷阵雨"
cheeseQuestion.answer()
//常量属性的修改:在构造方法内任何地方都可以给常量属性指定一个值,指定后常量的值将永远不可更改;类的实例中,常量属性不能再子类中修改。
class SurveyQuestion2 {
let text: String
var response: String?
init(text: String){
self.text = text
}
func ask() {
print("问:\(text)")
}
func answer(){
print("答:\(response ?? "没有回答")")
}
}
let beetsQuestion = SurveyQuestion2(text: "你多大了?")
beetsQuestion.ask()
beetsQuestion.response = "hahaha"
beetsQuestion.answer()
//=================================== 默认构造器 =========================================
//如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么Swift会给这些结构体或类提供一个默认构造器。
class ShoppingListItem2 {//购物清单
var name: String? //名字是可选的,默认值是nil
var quantity = 1 //数量默认1
var purchased = false //购买状态默认否
}
var item = ShoppingListItem2() //使用默认构造器创造了一个类的实例并赋值给item
//结构体的逐一成员构造器:用来初始化结构体新实例成员属性的快捷方法,通过与成员属性名相同的参数名来完成初始赋值
struct ZYSize {
var width = 0.0, height = 0.0
}
let twoByTwo = ZYSize(width: 2.0, height: 2.0)
//=================================== 值类型的构造器代理 ===================================
//构造器代理:构造器通过调用其他构造器来完成实例的部分构造过程,能减少多个构造器直接的代码重复。
//类构造器代理:类可以继承自其他类,所以类需要保证其继承的存储属性在构造是也能正确的初始化。
/**值类型构造器代理:因为不支持继承,所以只能代理给自己的其他构造器;可以使用self.init在自定义的构造器中引用相同类型中的其他构造器,并且只能在构造器内部调用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() {//第1种初始化:使用被初始化为默认值的origin 和 size属性来初始化
}
init(origin: Point, size: Size) {//第2种初始化:提供指定的 origin 和 size实例来初始化
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {//第3种初始化:提供指定的center 和 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()//第1种初始化:值为默认值: Point(x: 0.0, y: 0.0) Size(width: 0.0, height: 0.0)
let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))//第2种初始化:简单的将origin 和 size 的参数值赋给对应的存储型属性
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))//第3种初始化:通过center和size算出x和y,然后在调用init(origin: size:)完成初始化
//=================================== 类的继承和构造过程 ===================================
/**类里面的所有存储型属性(继承自父类、自有)都必须在构造过程中设置初始值。Swift为类类型提供了两种构造器来确保所有存储型属性都有初始值:指定构造器、便利构造器。
指定构造器:类中最主要的构造器,将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化
init(参数){
实现
}
便利构造器:类中次要辅助型的构造器,可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。
convenience init(参数){ //便利构造器需要在init关键字前加 convenience关键字
实现
}
类的构造器的代理规则:
1.指定构造器必须调用其直接父类的指定构造器;
2.便利构造器必须调用同类中定义的其他构造器;
3.便利构造器必须最终导致一个指定构造器被调用。
指定构造器必须总是向上代理;便利构造器必须总是横向代理。
两段式构造过程:Swift中类的构造过程分两个阶段:
第一阶段:
某个指定构造器或便利构造器被调用
完成新实例内存的分配,但此时内存还没有被初始化
指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化
指定构造器将调用父类的构造器,完成父类属性的初始化
这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。
当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时第一阶段完成。
第二阶段:
从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。
Swift编译器将执行4种有效的安全检查,以确保两段式构造过程能不出错地完成:
1.指定构造器必须保证它所在的类引入的所有属性都必须先初始化完成,之后才能将其它j构造任务向上代理给父类中的构造器。
2.指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。
3.便利构造器必须先代理调用同一类中的其他构造器,然后再为任意属性赋新值。
4.构造器在第一阶段构造完成之前,不能抵用任何实例方法,不能读取任何实例属性的值,不能引用self作为一个值。
*/
/** 构造器的继承和重写:
Swift中的子类在默认情况下不会继承父类的构造器,加入你希望子类中能提供一个或多个跟父类相同的构造器,可以在子类中重写父类的构造器,重写时必须带上override修饰符
由于子类不能直接调用父类的便利构造器,所以在子类中创建一个与父类便利构造器相同的构造器时可以不加override前缀
*/
class Vehicle {//车 : 类中只为存储型属性提供默认值,没有自定义构造器,因此,它会自动获得一个默认构造器
var numberOfWheels = 0 //车轮数
var description: String {
return "有\(numberOfWheels)个轮子"
}
}
let vehicle = Vehicle()
print(vehicle.description)
class Bicycle: Vehicle { //定义了一个继承Vehicle 的子类Bicycle,
override init() {//子类中定义了一个自定义指定构造器init(),由于这个指定构造器与父类的默认指定构造器相同,所以前面需要带上override 表示是重写父类的构造器
super.init() //以调用super.init()方法开始,作用是调用父类Vehicle的默认构造器,这样可以在确保Bicycle在修改属性前,它所继承的属性numberOfWheels能被Vehicle类初始化
numberOfWheels = 2 //父类初始化后,原值被新值替换(子类可以在初始化时修改继承来的变量属性,但不能修改继承来的常量属性)
}
}
let bicycle = Bicycle()
print("自行车\(bicycle.description)")
/**构造器的自动继承
子类在默认情况下不会继承父类的构造器,但如果为子类中引入所有新属性都提供了默认值,那么会适用以下2个规则:
1.如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器
2.如果子类提供了所有父类指定构造器的实现,无论是上一规则继承来的,还是提供了自定义实现,他将自动继承所有父类的便利构造器。
即使你再子类中添加了更多的便利构造器,这两条规则仍然适用。对于规则2,子类可以将父类的指定构造器实现为便利构造器。
*/
//指定构造器和便利构造器实践:
class Food { //食物名称:基类
var name: String
init(name: String) {//接受单一参数name的 指定构造器;Food类没有父类,所以不用调super.init()来完成构造过程
self.name = name
}
convenience init() { //convenience表示是便利构造器,这是一个无参数的便利构造器
self.init(name: "未知名称") //通过横向代理到指定构造器init(name: String)来给参数name传值来实现
}
}
let nameMeat = Food(name: "培根") // nameMeat 的名字是 "培根"
let mysteryMeat = Food() // mysteryMeat 的名字是 "未知名称"
class RecipeIngredient: Food { //食谱原料:继承自Food(食物名称)
var quantity: Int //量
init(name: String, quantity: Int) {//接受两个参数的指定构造器
self.quantity = quantity
super.init(name: name) // 因为RecipeIngredient类有父类,所以需要调用super.init(name: String)来完成构造过程
}
override convenience init(name: String) { //子类中重写父类的构造器时必须带上override修饰符,convenience表示是便利构造器(注意:子类RecipeIngredient在这里重写了父类所有的指定构造器的实现(变成了自己的便利构造器),所以子类RecipeIngredient也自动继承了父类的所有遍历构造器)
self.init(name: name, quantity: 1)
}
}
let oneMySteryItem = RecipeIngredient()//如果子类提供了所有父类指定构造器的实现,无论是上一规则继承来的,还是提供了自定义实现,他将自动继承所有父类的便利构造器。
let oneBacon = RecipeIngredient(name: "培根")//
let sixEggs = RecipeIngredient(name: "鸡蛋", quantity: 6)
class ShoppingListItem: RecipeIngredient {//购物清单,继承自RecipeIngredient (食谱原料)
var purchased = false //是否已购买
var description: String {
var output = "\(quantity) x \(name)" //输出示例:1 x 培根
output += purchased ? "✔" : "✘"
return output
}
//由于ShoppingListItem类为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,所以该类将自动继承所有父类中的指定构造器和便利构造器。
}
//可以使用全部三个继承来的构造器来创建ShoppingListItem的实例:
var breakfastList = [ShoppingListItem(),
ShoppingListItem(name: "火腿"),
ShoppingListItem(name: "鱼丸", quantity: 8),
] //创建了一个数组,里面放了三个ShoppingListItem 的实例
breakfastList[0].name = "橙汁" //将第一个实例 的name 由 "未知名称" 改为 "橙汁"
breakfastList[0].purchased = true //并显示 已购买
for item in breakfastList {
print(item.description)
/**打印的结果是:
1 x 橙汁 ✔
1 x 火腿 ✘
6 x 鱼丸 ✘
*/
}
//=================================== 可失败构造器 ========================================
/**如果给一个类、结构体或枚举类型的构造器传入无效的参数值,或没满足某种必要的条件,那么在构造的过程中就有可能会失败,这时就需要在其中添加一个或多个可失败构造器。
可失败构造器的语法为:在init关键字后面添加问号: init?
注意:可失败构造器的参数名和参数类型,不能与其他非可失败构造器的参数名及其参数类型相同。
可失败构造器会创建一个类型为自身类型的可选类型的对象。通过return nil语句来表明可失败构造器在何种情况下应该“失败”;因为,严格来说,构造器都不支持返回值,因为构造器本身的作用,只是为了确保对象能被正确的构造。因此你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功。
*/
//示例1:init(exactly:) 构造器 如果类型转换不能保持值不变,则这个构造器失败
let wholeNumber: Double = 12345.0
let pi = 3//3.14159;
if let valueMaintained = Int(exactly: wholeNumber) {
print("\(valueMaintained) 转换为Int的类型,保持了值不变")//12345.0 转换后的值为12345
}
let valueChanged = Int(exactly: pi)//此处,valueChanged是 Int?类型,不是Int类型
if valueChanged == nil {
print("\(pi)转换为Int类型,没有保持值不变")
}
//示例2:结构体 类型的可失败构造器
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty {
return nil
}
self.species = species
}
}
let someCreature = Animal(species: "加菲猫") //someCreature是 Animal? 类型 而不是 Animal类型;
if let giraffe = someCreature {
print("用\(giraffe)对此类进行了初始化")
}
let anonymousCreature = Animal(species:"") //空字符串和为nil的可选字符串是两个概念。
if anonymousCreature == nil {
print("无法初始化匿名生物。")
}
//枚举类型的可失败构造器
//可以通过一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。若提供的参数无法匹配任何枚举成员,则构造失败。
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("这是一个定义的温度单位,因此初始化成功。")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("这不是是一个定义的温度单位,因此初始化失败。")
}
if let unknownUnit2 = TemperatureUnit(symbol: "X") {
print("\(unknownUnit2)是温度单位")//unknownUnit2不为nil才会走进了
}
//带原始值的枚举类型的可失败构造器
//带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的参数,其类型和枚举类型的原始值一致,如果和原始值匹配,则该构造器会构造响应的枚举成员,否则构造失败。
//重写上面的例子:
enum TemperatureUnit2: Character {
case Kelvin = "K",Celsius = "C",Fahrenheit = "F"
}
let fahrenheitUnit2 = TemperatureUnit2(rawValue:"F")
if fahrenheitUnit2 != nil {
print("2这是一个定义的温度单位,因此初始化成功。")
}
let unknownUnit2 = TemperatureUnit2(rawValue:"X")
if unknownUnit2 == nil {
print("2这不是是一个定义的温度单位,因此初始化失败。")
}
//构造失败的传递
//类、结构体、枚举的可失败构造器可以横向代理到类型中的其他可失败构造器。同样,子类的可失败构造器也能向上代理到w父类的可失败构造器。
//无论是向上代理还是横向代理,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码都不会再被执行
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) {//如果购物车中商品的量 小于1 构造失败
if quantity < 1 {
return nil
}
self.quantity = quantity
super.init(name: name)//调用父类,接着构造name
}
}
if let twoSocks = CartItem(name: "袜子", quantity: 2) {
print("商品名称为:\(twoSocks.name),购买数量为:\(twoSocks.quantity)")
}
if let noProductName = CartItem(name: "", quantity: 1) {//不会构造成功
print("商品名称为空,购买数量为:\(noProductName.quantity)")//不会执行
}else{
print("构造失败,因为没有给商品名称赋值")
}
if let zeroSocks = CartItem(name: "袜子", quantity: 0){
print("构造成功,那是不可能的:\(zeroSocks.name)")//不会执行
}else{
print("构造失败,因为没有给商品数量赋值")
}
//重写一个可失败构造器
//可以在子类中重写父类的可失败k构造器,或者可以用子类的非可失败构造器重写一个父类的可失败构造器,这样可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败
class Document {
var name: String? //name值为nil
init() {
}
init?(name: String){
self.name = name
if name.isEmpty {
return nil
}
}
}
class AutomaticallyNamedDocument: Document {
override init() {//重写父类的构造器,并强制将name赋值
super.init()
self.name = "未知名称"
}
override init(name: String) {//重写父类的构造器,若父类name为空则强制将name赋值
super.init()
if name.isEmpty {
self.name = "未知名称2"
}else {
self.name = name
}
}
}
class UntitledDocument: Document {
override init() {
super.init(name: "未知名称3")! //对父类的构造器强制解包,使子类的name属性的值总是”未知名称3“
}
}
//可失败构造器init!
//通常定义可失败构造器是:init?
//但也可以通过 init! 来定义一个可失败构造器,该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象;一旦 init!构造失败,则会触发一个断言。
//=================================== 必要构造器 ==========================================
//在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器
class SomeClassMust {
required init() {
//构造器实现的代码
}
}
class SomeSubClassMust: SomeClassMust {//在子类重写父类的必要构造器是,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器是,不需要添加override修饰符。
required init() {
//构造器实现的代码
}//如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现。
}
//=================================== 通过闭包或函数设置属性的默认值 ==========================
//如果某个存储型属性的默认值需要一些定制或设置,可以使用闭包或全局函数为其提供定制的默认值。这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足语气的h初始状态,最后返回这个临时变量,作为属性的默认值。
//class SomeClass {
// let someProperty: SomeType = {
//在这个闭包中 给 someProperty创建一个默认值
// return someValue //someValue必须和someType类型相同
// }() // 注意:闭包结尾处大括号后面跟了一对空的小括号,这是用来告诉Swift立即执行此闭包。如果忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
//}
//示例:西洋跳棋棋盘
struct Checkerboard {
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//遍历完成后得到一个8*8的颜色数组
}()
func squareIsBlackAtRow(row: Int, column: Int) -> Bool { // 传入行和列,得到 正方形是黑色的 Bool值
return boardColors[(row * 8) + column]
}
}
let board = Checkerboard()
print(board.squareIsBlackAtRow(row: 0, column: 1))
print(board.squareIsBlackAtRow(row: 7, column: 7))
16.Swift构造过程
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 本章将会介绍 存储属性的初始赋值自定义构造过程默认构造器值类型的构造器代理类的继承和构造过程可失败构造器必要构造器...
- swift构造过程总结系列是通过阅读swift官方文档后自己的一些总结。阅读过官方文档的同学都知道,在swift构...