iOS Swift No.14 - 构造 5

第十四章 构造

6. Failable Initializers (可失败构造器)

我们有时候可以为一个类,结构体或者枚举定义一个可失败的构造器。也就是说为类,结构体或者枚举属性值在构造过程中可能会失败,这种失败的行为可能会被一个在构造过程中传入一个无效的参数值,一个该类型所需要的外部资源或其他可预防构造过程成功的条件所触发。这三种行为均可导致该类型的构造器失败,

可以用init?作为一个可选类型返回nil的构造器来介绍并引出这个可失败的构造器。可以类,结构体或枚举定义里面的一个或多个构造器定义为可失败的构造器(可选类型的构造器)。来指明该类型的失败构造过程可以被某种行为所触发。

下面的实例可以说明失败构造器的用途,一个失败构造器可以用来做数字类型的转换,使用init(exactly: ) 构造器来确定数字类型中是否存在一个具体值。如果说这个转换不成立 那么就可以说这个构造器失败了(完整的构造过程不能被触发)。下面的例子会详细说明。

let wholeNumber: Double = 12345.0
let pi = 3.14159
/* wholeNumber参数名 Double参数类型 exactly实参标签
	if let为可选绑定详情参考前几章 
	用if let来强制把一个类型为Double的wholeNumber转化为Int类型的wholeNumber */
if let valueMaintained = Int(exactly: wholeNumber) {
    print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
/* 实例化后的调用会导致构造器过程失败
	构造过程失败会有三种行为所触发
	在构造过程中传入一个无效的值时会触发
	所以该失败构造器会返回nil
	原本该传入的值是一个整数Int
	但pi在定义的时候却是小数 
	故失败构造器被传入的无效值触发*/
let valueChanged = Int(exactly: pi)
/* 这个转换数字类型的构造器,构造过程失败了 他会返回nil
	用if条件语句来判断*/
if valueChanged == nil {
	// 如果这个构造器失败 就会输出下面这行 反之则不输出
    print("\(pi) conversion to Int does not maintain value")
}

下面这个例子定义了一个结构体Animal,和一个String类型的常量属性,一个类型为String的空参数失败构造器,因为呢这个构造器的参数为空所以失败构造过程会被触发。除非这个结构体的属性species的空白值可以重新设置,这样整个构造过程才会成功。

struct Animal {
	// 结构体的常量 没有给出值的情况下 默认是空白(Empty)值
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}
// 用失败构造器构造一个新的Animal的实例 来检查该构造过程是否成功。
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
    print("An animal was initialized with a species of \(giraffe.species)")
}
// 构造过程的成功与失败 取决于构造器中传入的参数是否为空值 
// 参数为空值则触发失败构造器,反之则不触发 说明整个构造过程可以进行
let anonymousCreature = Animal(species: "")
if anonymousCreature == nil {
    print("The anonymous creature could not be initialized")
}

6.1 Failable Initializers for Enumerations (枚举中的失败构造器)

我们可以基于枚举成员中的一个或者多个参数来使用失败构造器,如果所提供的参数没有匹配到枚举成员,那么这个时候整个构造过程就回失败。

下面这个例子定义了一个枚举TemperatureUnit,和三种不同的状态(kelvin celsius fahrenheit),和一个用来找出某个字符(Character)是否代表这三个温度符号的失败构造器。

enum TemperatureUnit {
    case kelvin, celsius, fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .kelvin
        case "C":
            self = .celsius
        case "F":
            self = .fahrenheit
        // 默认情况下 任何参数(非KCF)都会触发失败构造器
        default:
            return nil
        }
    }
}
// 只要参数是 枚举成员中的任意一个 都不会出发失败构造器,
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
    print("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.")
}

6.2 Failable Initializers for Enumerations with Raw Values(原始值的枚举中的失败构造器)

含有原始值的枚举回自动接受失败构造器。init?(rawValue: )构造器会选用一个叫rawValue的原始值类型的参数。用这个参数来匹配枚举中的任何一个成员值。只要能匹配到这个成员值则说明该构造器可以构造参数,反之则说明了 该枚举的构造过程失败,会触发失败构造器并返回nil。

enum TemperatureUnit: Character {
    case kelvin = "K", celsius = "C", fahrenheit = "F"
}
// 实例化1 参数可以匹配到枚举中的成员
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
	// 则说明构造过程成功 
    print("This is a defined temperature unit, so initialization succeeded.")
}
// 实例化2 参数没能匹配到枚举中的任何一个成员。匹配到默认
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
	// 则会触发到失败构造器
    print("This is not a defined temperature unit, so initialization failed.")
}

6.3 Propagation of Initialization Failure (失败构造过程的传播)

一个类,结构体或者枚举的失败构造器可以横向代理给相同的类,结构体或者枚举,同样的,一个子类的失败构造器可以向上代理给他的父类的失败构造器。这就是失败的构造过程的传播途径和规则。

就像上面说的那样 不管是横向代理还是向上代理,只要它是一个失败的构造器 那么不管它是哪一种方式的代理(横向或向上代理) 这个构造器在都会将原有的状态(失败构造过程)传播给另一个同类型中的构造器。一旦前一个构造过程失败 那么将要传播给的那个构造器的构造过程将不会执行。
在这里插入图片描述
下面这个例子 定义了一个父类Product和一个子类CartItem,向我们呈现了一个商品和购物车的状态。父类里面的构造器init?(name: String)主要是用来检查这个商品的名字可否呈现出来,可以呈现则会继续执行子类里面构造器的代码。父类一旦出现商品名字无法呈现则构造器失败,进来不会执行子类向上代理父类的构造器。

class Product {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
// 子类CartItem继承自父类Product
class CartItem: Product {
	// 默认购物车里面商品数量是1个 
    let quantity: Int
    //  子类的构造器 
    init?(name: String, quantity: Int) {
        if quantity < 1 { return nil }
        self.quantity = quantity
        // 构造器向上代理 指向父类
        super.init(name: name)
    }
}

下面这个例子,就是实例化 用来检测商品的名字是否有值或可否呈现,如果商品的名字无值,空白值或商品数量为0的情况下都会导致代理失败构造过程的从一个类传播到另一个类。

// 商品有名有数量 不会触发失败构造器 也不会传播失败的构造过程
if let twoSocks = CartItem(name: "sock", quantity: 2) {
    print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// 输出: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")
}
// 输出: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")
}
// 输出:Unable to initialize one unnamed product

猜你喜欢

转载自blog.csdn.net/weixin_45026183/article/details/105938975