[Swift基础语法入门] Initialization详解

开篇

声明一个类、结构体或枚举,就像绘制了一副草图,即使描绘得再栩栩如生,也仅跃于纸上罢了。

举例来说,描述一辆汽车:车型、车身颜色、出厂日期等等。为此声明一个 Car 的类:

class Car{
  var name:String    /**> 车辆型号 */
  var color:String   /**> 车身颜色 */
  var date:NSDate    /**> 出厂日期 */
  // 当然还有其他属性
}

此时手上只有Car的设计图,这并不意味着你已经拥有一辆保时捷或法拉利可以去兜风了;所以喽,还是将设计稿图交给工厂,由他们按照设计图制造一辆货真价实的小汽车交付给你。ps:制造出来的车在编程中叫实例(顾名思义:实际的例子)

等等,工厂拒绝了!拒绝理由:车型没有指明,喷什么颜色呢?

修改如下:

class Car{
  var name:String = "保时捷911"   /**> 车辆型号 */
  var color:String = "红色"       /**> 车身颜色 */
  var date:NSDate = NSDate()     /**> 出厂日期 */
  // 当然还有其他属性
}

ok!默认车型“保时捷”、车身颜色“红色”,出厂日期为加工当天。

独乐乐不如众乐乐,于是大手一挥,再加工一打!

厂家:还是全红色的保时捷911?

当初想得过于简单,设计稿图参数全给了默认值,失策失策!
修改如下:

class Car{
  var name:String        /**> 车辆型号 */
  var color:String       /**> 车身颜色 */
  var date:NSDate        /**> 出厂日期 */
  // 当然还有其他属性
  
  init(name:String,color:String){
    self.name = name
    self.color = color
    date = NSDate()
  }
}

至此,告诉厂家:第一辆要蓝色的保时捷911,第二辆要红色的保时捷Cayenne,第三辆...

几种类型的Initialization

枚举:

enum Direction{
    case East, South, West, North
    
    init(symbol:Character){
        switch symbol{
            case "E":
                self = .East
            case "S":
                self = .South
            case "W":
                self = .West
            case "N":
                self = .North
            default:
                self = .East
        }
    }
}

let dir = Direction(symbol: "S") // .South

结构体1:

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
let temp = Fahrenheit()

结构体2:

struct User {
    var username:String
    var password:String
    
}
// 尽管没有声明初始化方法 但是对于结构体默认是有memberwise initialization
let user = User(username: "test", password: "123456")

类:

// 类初始化
class Car{
    var name:String
    var color:String
    var age:Int
    
    init(name:String,color:String,age:Int){
        self.name = name
        self.color = color
        self.age = age
    }
}

何为Initialization

根据设计稿图制造出车辆实例,在车辆出厂使用前,必须确认车辆车型和为车身喷漆,这是一个 Initialization 过程,保证车辆实例使用前完成所有的准备工作。

官方文档对此的描述是:

This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.

关键词:stored property存储属性,换句话说对于computed property计算属性是排除在外的。

再来说说 Swift 中 Initialization 的声明形式,使用init修饰符即可:

init() {
    // perform some initialization here
}

默认值

倘若你偏爱红色的保时捷,那么你可以一开始就为实例属性color设定默认属性为“红色”

class Car{
  // ...
  var color:String = "红色"       /**> 车身颜色 */
  // ...
}

自定义

倘若你天马星空,那么你就应该声明一个自定义的构造方法,传入配色和车型来实例化车辆。

class Car{
  var name:String        /**> 车辆型号 */
  var color:String       /**> 车身颜色 */
  var date:NSDate        /**> 出厂日期 */
  // 当然还有其他属性
  
  init(name:String,color:String){
    self.name = name
    self.color = color
    date = NSDate()
  }
}

局部和外部参数名

当然就像函数(function)和方法(method)一样,构造方法也是可以拥有local name 和 external name。

Swift 自动为每一个参数提供 external parameter 名字,换句话说你丫别提供了!!

来自官方文档的例子:

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red //这里的red green blue 是局部参数名
        self.green = green
        self.blue  = blue
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
}
// 这里的red green blue 是外部参数名字
// 这个是很有必要的,否则调用者怎么知道我传入的1.0 0.0 1.0 到底赋值给哪个参数了呢!!!
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)

注意到redbluegreen 即使local parameters,能够在init中使用;又可以在构造方法调用时作为external parameters 呈现

总有些“捣蛋鬼”就是想反其道而行:不要外部标签可以吗?? 好吧,答案自然是ok。使用** _ **下划线占据external parameter name的位置,告知编译器在调用构造方法时忽略外部参数名:

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

实例变量是可选参数类型

说说车辆的牌照,车辆刚制造出来时,对于车牌没有强制要求,一个月后上牌就OK。所以喽,我们可以为Car设定一个可选类型的车牌实例变量:

class Car{
  var licensePlate:String? /**> 因为车牌一开始可以是没有的
  var name:String        /**> 车辆型号 */
  var color:String       /**> 车身颜色 */
  var date:NSDate        /**> 出厂日期 */
  // 当然还有其他属性
  
  init(name:String,color:String){
    self.name = name
    self.color = color
    date = NSDate()
  }
}
let car = Car(name:"保时捷911",color:"红色")
// 摇到号了 
car.licensePlate = "浙A123456"

常量属性

让我们在“计较”点,车辆车型选定后,之后是绝无修改的可能,要知道改装车辆是违法的,所以喽,将name 声明为常量是一个明智的选择。

class Car{
  var licensePlate:String? /**> 因为车牌一开始可以是没有的
  let name:String        /**> 车辆型号 */
  var color:String       /**> 车身颜色 */
  var date:NSDate        /**> 出厂日期 */
  // 当然还有其他属性
  
  init(name:String,color:String){
    self.name = name
    self.color = color
    date = NSDate()
  }
}

默认的 Initializers

Swift 为以下对象提供默认构造方法:

  • Structure(结构体)
  • 所有实例属性均设置了默认值的Class(类)

以官方文档为例:

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()

对于类来说,我们并未写一个init方法,而是给定每一个实例属性默认值。
结构体有点特殊,无须为实例属性设定默认值,先看看结构体的声明:

struct StructTest{
  // memberwise 逐个成员初始化呗
  var para1:String
  var para2:String
  var para3:String
  var para4:String
}

注意我们并未给任何一个参数设定初始值,但实际上结构体已经就自定 memberwise initializer了(memberwise:逐个成员的意思),即生成了init(para1:,para2:,para3:,para4:)构造方法了!

值类型中的 Initializer Delegation

对于值类型(结构体和枚举)来说,是没有继承的,这意味着只允许构造方法之间的互相调用,即使用self.init,而没有super.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()
    // 1
    init() {}
    // 2
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    // 3
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        
        // 至于这里调用self.init() 无非就是不想在写self.origin = origin和self.size = size 
        // 由于这里初始化代码简单,所以作用感觉不出,但是某些情况下确实可以节省代码量
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

1、2 和 3 都是构造方法,其中1中,由于我们为结构体中的所有成员都给定了默认值,因为init(){}方法中什么都没写,但是倘若我们未给结构体成员默认值,那么init(){}报错Return from initializer without all stored properties 很好理解,构造方法的职责就是为所有实例变量在使用前设定初始值!假若你没有那么做,自然报错喽。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容