swift4.1 系统学习十三 结构体(二)

//
// main.swift
// swift14(结构体)
//
// Created by iOS on 2018/10/15.
// Copyright © 2018年 weiman. All rights reserved.
//

import Foundation

// 结构体(二)
/*
继续上节内容,学习结构体的相关知识。
上一节的学习中,我们主要学习了结构体中的属性,包括:
1.存储式实例属性
2.惰性存储式属性
3.计算式属性
4.属性观察者
5.类型属性

本节,我们来学习结构体中的方法,包括:
1.实例方法
2.类型方法
3.初始化器方法
4.逐成员的初始化器方法
5.值类型的初始化器代理
6.可失败的初始化器
7.下标语法
*/

// 1. 实例方法
/*
定义:当我们在枚举、类、结构体类型中定义一个函数时,该函数被称为“方法”。

每个实例方法都具有一个隐式的属性 self ,它指向调用此方法的对象实例,所以self的类型为当前对象的类型。
*/

do {

struct Test {
    // 存储属性
    var a = 10
    
    let s = "hello"
    
    func printS() {
        // 这里,self可以省略。
        print("a = \(a)")
        print("s = \(s)")
    }
    
    // 由于这个方法中,对实例属性进行了修改,所以需要加上mutating关键字。
    mutating func method2(a: Int) {
        // 这里的参数 a 与 实例属性 a重名,所以需要加上self加以区分。
        self.a += a + 1
    }
    
    // 将关联的对象实例重新修改为默认状态。
    mutating func method3() {
        self = Test()
    }
}

var test = Test()
test.method2(a: 10)
test.printS()
test.method3()
test.printS()

}

// 实例方法的引用
/*
由于实例方法必须要与某一对象实例进行关联,所以我们用一个函数引用对象指向某一对象实例的方法时
需要将对象实例也一起带上。
*/

do {

print("\n")

struct Test {
    var property = 100
    
    mutating func method(a: Int) {
        property += a
    }
    
    func foo(_ : Void = ()) {
        print("property = \(property)")
    }
    
    func foo(a: Int) {
        print("value = \(property + a)")
    }
}

var test = Test()
test.property += 10

// 这里通过method方法签名来对它进行调用。
// 由于mutating方法不允许通过函数引用对象对它进行引用,
// 所以这里只能直接通过方法签名做直接调用,
// 这是允许的。
test.method(a:)(5)

let ref = { test.method(a: 6) }
ref()

let ref1 = test.foo(_:)
ref1(())

let ref2 = test.foo(a: )
ref2(10)

/*
 小结:
 对实例方法的引用必须包含与它关联的对象实例。
 */

}

// 2. 类型方法
/*
类型方法与类型属性类似,是与类型相关联的方法,而不是对象实例。
定义一个类型方法也很简单,直接在func 前面添加static就可以了。
如果当前类型是类类型,那么我们还能使用class关键字修饰,表示当前类型方法能够被子类重写。
如果在类类型中用了static关键字修饰,那么该类型方法不允许被子类重写。
*/

do {

print("\n")

struct Test {
    static var a = 100
    
    /// 类型方法
    static func method() {
        // 可以修改类型属性
        self.a += 20
        print("method: a = \(a)")
    }
    
    static func getValue(a: Int) -> Int {
        return self.a + a
    }
    
    static func foo(_: Void) {
        print("这是一个foo")
    }
    
    /// 重载方法
    static func foo(a: Int) {
        print("a = \(a)")
    }
}

// 调用类型方法method
Test.method()

let a = Test.getValue(a: 5)
print("a = \(a)")

var ref = Test.foo(_:)
ref(())

let ref2 = Test.foo(a: )
ref2(1)

/*
 打印结果:
 method: a = 120
 a = 125
 这是一个foo
 a = 1
 */

}

// 3. 初始化器方法
/*
初始化器方法用于在创建一个类、结构体或者枚举类型的对象实例时为该对象的实例属性进行初始化。
在swift中使用init关键字表示当前类型的初始化器方法,然后后面跟着形参列表。
注意:
由于一个类型的初始化器方法肯定返回它所创建的对象实例,因此其返回类型不需要写,就是当前类型本身。
*/

do {

struct Test {
    var a = 10
    let b: Float
    var c: String
    var d: Int?
    
    init() {
        b = 1.0
        self.c = "Hello"
    }
}
// 省略init
var test = Test()
test = Test.init()

let ref = Test.init
test = ref()

}

// 4. 逐成员的初始化器方法
/*
对于结构体类型,有一种默认的初始化形式,叫做逐成员的初始化器方法。
当我们在结构体中定义了一些存储式属性,并没有对他们进行初始化,也没有显示的提供初始化器方法,那么
我们在用该结构体去创建一个对象实例时就可以使用逐成员的初始化方法来为该结构体对象中的每个存储式实例属性
进行指定具体的值。
*/

do {

print("\n")

struct Test: CustomStringConvertible {
    var a = 10
    let b: Float
    var c: String
    var d: Int?
    
    var description: String {
        return "a = \(a), b = \(b), c = \(c), d = \(d)"
    }
}
// 逐成员的初始化器方法
let test = Test.init(a: 10, b: 1.9, c: "生活在社会底层,也要努力活着。", d: 8)
print("test : \(test)")

}

// 5. 值类型的初始化器代理
/*
当我们在一个初始化器方法中调用另一个初始化器方法以执行对一个对象实例的部分初始化,那么这个过程就
叫做初始化器代理。
*/

do {

print("\n")

struct Test {
    var a = 10
    let b: Float
    var c: String
    var d: Int?
    
    init(b: Float) {
        self.b = b
        c = ""
    }
    
    init(b: Float, c: String) {
        self.init(b: b)
        self.c = c
    }
    
    init(b: Float, c: String, d: Int) {
        self.init(b: b, c: c)
        self.d = d
    }
}

var test = Test.init(b: 1.0)
test = Test.init(b: 2.0, c: "OK")
print("test: \(test)")
test = Test.init(b: 3.4, c: "哈啊哈哈", d: 8)
print("test: \(test)")

}

// 6. 可失败的初始化器
/*
有时候需要定义某些类型,这些类型根据用户的输入或者当前的执行环境可能造成其对象实例的创建失败,此时
我们可以使用“可失败的初始化器”。
可失败的初始化器可根据当前条件返回空值。因此,当我们使用可失败的初始化器来创建一个对象时,该对象的
类型为Optional类型。
*/

do {

print("\n")

struct Test {
    var a: Int
    
    init? (value: Int) {
        if value == 0 {
            return nil
        }
        
        a = 100 / value
    }
}

let test = Test(value: 0)
if test == nil {
    print("failed")
} else {
    print("test: \(test)")
}

}

// 7. 下标语法
/*
swift语言中,允许我们在自定义类型中使用下标。
*/

do {

print("\n")

struct Test {
    // 存储式实例属性a
    var a = 10
    
    // 定义下标方法
    subscript(index: Int) -> Int {
        
        get {
            return a + index
        }
        
        set(value) {
            a = value + index
        }
    }
    
    subscript(str: String) -> Int {
        return str.count
    }
    
    subscript(value: Int, str: String) -> Int? {
        get {
            guard let count = Int(str) else {
                return nil
            }
            
            return value + count
        }
        
        set {
            if let data = newValue, let strValue = Int(str) {
                a = value + data + strValue
            }
        }
    }
    
    subscript(_: Void) -> Int {
        
        get {
            return a
        }
        
        set {
            a = newValue
        }
    }
}

var test = Test()
/// 这里调用了test对象的小标方法subscript(index: Int)的setter方法
test[1] = 10
print("test[5] = \(test[5])")
//打印:test[5] = 16

print("count = \(test["abc"])")

test[2, "123"] = 100

print("test: \(test)")

}

// 8. key path
/*
有时候,一个结构体、枚举或者类类型中的某个属性的类型比较复杂,类型嵌套比较深,在swift4中,引入了
Smart KeyPaths这一概念来简化对一些嵌套比较深的属性访问。

*/

do {

print("\n")

struct MyRect {
    
    struct Point {
        var x: Float
        var y: Float
    }
    
    struct Size {
        var width: Float
        var height: Float
    }
    
    var position: Point
    
    var size: Size
}

struct MyStruct {
    var property: Int
    var rect: MyRect
}

/// 这里用Smart KeyPath字面量
/// 定义一个widthKeyPath关键路径,
/// 它是对MyStruct.rect.size.width
/// 这一实例属性的访问路径
let widthKeyPath = \MyStruct.rect.size.width

var obj = MyStruct(property: 10, rect: MyRect(position: MyRect.Point(x: 0.0, y: 1.0), size: MyRect.Size(width: 10.0, height: 20.0)))
let width = obj[keyPath: widthKeyPath]
print("width: \(width)")

obj[keyPath: \MyStruct.rect.position.x] += obj.rect.position.y * 8.0
print("x = \(obj.rect.position.x)")

}

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

推荐阅读更多精彩内容