Swift 学习笔记---Initialization

一.建立存储属性的初始化值

 当获得结构体和类的实例必须给他们的存储属性设置初始化值。你有两种方法来初始化。一种方法设置初始化器,
 另一种方法是设置默认值。当给一个存储属性初始化的时候,不会调用观察者方法。
Initializers
 当获得一个类型的实例时,初始化器被自动调用。一个初始化器像一个实例方法。我们用 init 
 关键字来表示初始化方法。
 init() {
// perform some initialization here
}

struct Fahrenheit {
var temperature: Double
init() {
    temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° 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( red: Double) {
    self.red   = red
    self.green = 0.0
    self.blue  = 1.0
}


init(red: Int, green: Double, blue: Double) {
    self.red   = 0.0
    self.green = green
    self.blue  = blue
}

init(yellow:Double,green:Double,blue:Double){
    self.red = yellow
    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
调用初始化函数必须加上提示标签。
可选的属性类别
 存储属性在逻辑上容许没有值,这可能是因为在初始化期间无法对其赋值或者他是故意的不进行赋值,
 这种情况下我们会声明其为可选属性。可选属性被初始化为 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.)"

默认初始化器

 当我们没有提供给结构体或者类一个初始化器的时候,Swift 会自动添加一个默认的初始化器。
 默认的初始化器只是简单的返回创造一个新的实例对象。对象属性的值全部为我们设定的默认值。

 class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
结构体成员初始化
结构体有一个特殊初始化器叫做逐一初始化器,它带有参数,初始化器用这些参数来逐一初始化结构体里的属性,
因此,我们可以不提供属性的默认值。

struct Size {
var width :Double
var height:Double
}
let twoByTwo = Size(width: 2.0, height: 2.0)

值类型的初始化代理

 初始化器可以调用其他初始化器,这个过程叫做初始化代理。
 初始化代理的工作规则和初始化代理的形式,在值类型和类类型下是不同的。
 值类型不支持继承,
 因此他们的初始化代理相对简单。因为他们只能代表他们自己提供一个初始化。然而类可以继承于其他类,这个意味着他有责任初始化父类的存储属性。
 对于值类型,我们可以用 self.init 来引用其他初始化器,但是我们必须在一个初始化器内部来调用 self.init
  如果你有自定义的初始化器,那么编译器将不会再为你添加默认的初始化器。当然如果你非要保留默认初始化器,
  我们可以把我们自己的初始化写到分类里。

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)
}
}

类的继承和初始化

  全部的存储属性包括从父类继承而来,必须在初始化期间赋予一个初始化值。
  Swift 能提供两种方式确保全部的存储属性收到一个初始化值。他们被叫做指定初始化器和便利初始化器。
指定初始化器和便利初始化器
  指定初始化器是主要的初始化器。指定初始化器初始化全部的存储属性。他沿着继承链,调用父类的初始化器。
  每个类必须至少有一个指定初始化器。在许多情况下,这个必要条件被满足,通过继承一个或者更多的指定初始化器。
  便利初始化器是个副手。你能定义一个便利初始化器,这个初始化器可以为指定初始化器所需的参数设置默认值,
  然后调用指定初始化器。
  指定的初始化器:
  init(parameters) {
     statements
}
  方便初始化器:
  convenience init(parameters) {
      statements
}
类的初始化代理
  为了简化两种初始化器之间的关系,Swift 代理更加下面三种规则来调用两种初始化器
  1.一个特定初始化器必须调用它父类的初始化器
  2.便利初始化器必须调用本类中另一个初始化器
  3.一个变量初始化器必须最终调用一个特定初始化器。

可以用下图表示上面的规则:
这里写图片描述

两步初始化
  类的初始化有两个过程。在第一个阶段,每个存储属性被赋予一个初始化值。一旦初始化过程被完成,
  第二阶段开始,每个类都有一个机会再次改变存储属性的值。
  Swift的初始化过程更 Objective-c 是相似的。主要的不同在于OC 中的属性在第一阶段初始化过程中,
  被赋予0或者 nil。而 Swift 必须自己给予的初始化值(也可以不给,但是必须在初始化器中对其进行初始化,否则报错)。

   Swift 编译器执行4步安全检查来确保初始化过程完成。

   Sagety check 1

   一个指定的初始化器必须在调用父类的初始化器前,把本类的属性全部初始化。
   Sagety check 2
   指定初始化器在对父类的属性进行赋值之前,必须调用父类的初始化器,否则父类的初始化器会对属性值进行覆盖。

   Sagety check 3
   一个便利初始化器,在对属性赋值之前,必须先调用其他的初始化器,否则,赋予的值会被覆盖掉。

   Sagety check 4
   在第一阶段完成之前,不能调用任何实例方法,读取实例属性或者引用 self。
   下面是两步初始化的执行过程:
   Phase 1
      ·一个特定或者便利初始化器被调用
      ·内存为一个对象分配空间
      ·一个特定初始化器确保全部的存储属性有个初始化值。内存中的这些存储属性被初始化。
      ·特定初始化器调用父类的初始化器执行相同的任务。
      ·继续沿着继承连直到到底顶层的类。
      ·一旦到达根类,并且根类确保他的存储属性全部被初始化,那么内存被完全初始化,第一阶段完成

    Phase 2
       ·从继承者连返回,每个继承者连可以选择自定义这个实例。初始化器可以访问 self,修改属性,调用实例方法等等。
       ·最终,每个便利初始化器可以修改实例。

下图演示了 Phase 1的调用过程:
这里写图片描述
下图演示了 Phase 2的调用过程:
这里写图片描述

自动继承初始化器
 子类一般不会继承父类的初始化器。假设你给予你新增的属性默认值,在下面两种情况下子类会自动继承父类的初始化器。
 Rule1:
       如果你的子类没有定义任何指定初始化器,它将自动继承全部父类的初始化器。
 Rule2:
       如果你的子类覆盖了父类全部的指定初始化器或者在 Rule1继承父类的指定初始化器,
       子类将会自动继承父类的便利初始化器。子类可以用便利初始化器来覆盖父类的指定初始化器。
    例子:
    class Food {
var name: String
init(name: String) {
    self.name = name
}
convenience init() {
    self.init(name: "[Unnamed]")
}
}

   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 、structure、eneumeration 初始化可以失败的类型,
 我们可以定义他们的可以失败的初始化器。参数的错误,缺少额外的资源等都可能造成初始化过程的失败。
 为了应对可能初始化失败的情况,我们可以定义一个或者更多的失败初始化器。
 一个失败初始化器只需在 init 后加个?(init?),但是注意你不可以定义一个失败初始化器和一个非失败的初始化器,有一样的参数类型和名字。
 失败的初始化器可以返回一个可选类型。在失败初始化器中 return nil 就代表着初始化失败。
 但是你不能用 return 代表初始化成功。

 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."

枚举类型里其实本来就有一个失败初始化器,init?(rawValue:),它接受一个 rawValue 的参数,然后在枚举值中找有没有对应的枚举值,如果没有找到则会初始化失败。
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)")
}

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
        }
    }
}
init!失败初始化器
这种初始化器会自动解包,子类也可以重载或者调用它

必要初始化器

写有关键字 required 的初始化器,代表着子类必须实现这个初始化器:


class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}
子类也必须在实现父类的初始化上加 required 关键字,而不必写上 override


class SomeSubclass: SomeClass {
    required init() {
        // subclass implementation of the required initializer goes here
    }
}

为一个闭包或者函数设置默认属性

如果一个存储属性需要对默认值进行特殊的定制,我们可以用一个闭包或者一个全局函数去提供一个默认值。
当一个新的对象实例被初始化的时候,闭包或者函数将会被调用,他们会给属性返回一个默认值。
这种函数或者闭包会产生一个临时的值,这个值会作为属性的默认值
下面是一个展示如何利用闭包给属性默认值



class SomeClass {
    let someProperty: SomeType = {
       //someValue 必须是 SomeType 类型
        return someValue
    }()
}
注意用闭包初始化的时候,不能引用类里的方法或者属性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值