从零学习Swift 10: 可选链,协议,元类型

总结
一: 可选链

之前写的代码都是这样调用:

如果person是可选类型,就会报错:

解决这种报错有两种方式,一种是在person后面加感叹号!,进行强制解包:

使用感叹号!强制解包这种做法不安全,一旦person对象为nil,程序就会崩溃.

另一种是加问号?:

?的意思时会先判断person对象是否为nil,如果为nil,就不会再调用age,直接返回nil;如果person对象不为nil,才会调用age,正常返回;那什么类型既能接受nil,又能接受Int呢?当然是可选项类型.

变量age是可选项类型

所以,只要通过person?调用的属性,方法,下标等等,它们的结果要么是nil,要么就被包装成可选项类型:


class Dog{
    var name: String = "大黄"
}

class Cat{
    var color: String = "橘色"
}

class Person{
    var name: String = ""
    var age: Int = 0
    var dog: Dog = Dog()
    var cat: Cat? = Cat()
    func eat(){
    print("eat")
    }
    
    func height() -> Int{
        180
    }
    
    subscript(index: Int) -> Int{
        index
    }
    
}

var person: Person? = nil
var name = person?.name //String?
var age = person?.age//Int?
var color = person?.cat?.color //String?
var height = person?.height()// Int?
var index = person?[3] // Int?

上面代码person?.cat?.color使用多个?链接在一起就被称为可选链.如果可选链的任何一个节点为nil,整个调用就会失败.

所以我们拿到调用结果使用的时候就要解包了,一般用可选项绑定判断一下:


if let a = age{
    print("age 的值为",a)
}else{
    print("age 为nil")
}

同样也可以判断方法调用是否成功:


if let result = person?.eat(){
    print("调用成功",result)
}else{
    print("调用失败")
}

eat()方法并没有返回值,为什么也可以用可选项绑定来判断呢.因为无返回值就是Void,而Void的本质就是空元组.所以如果eat()调用成功就会返回一个空元组,调用失败就返回nil.所以可以使用可选项绑定判断.

思考一下下面代码的打印结果:


var a: Int? = 10
a? = 5
print("a",a)

var b: Int? = nil
b? = 5
print("b",b)


打印结果为a Optional(5), b nil;因为b?会先判断b是否为nil,如果为nil就不会执行= 5的赋值运算.

二: 协议(Protocol)

swift 中的协议可以定义属性 , 方法 , 下标的声明,协议可以被结构体,枚举,类遵守.

我们在定义协议时要遵守以下规则:

  1. 协议定义属性时只能用 var , 不能用 let
协议中的属性不能使用 let
  1. 实现协议时的属性权限要不小于协议中定义的属性权限
实现协议中的属性时,权限小于协议中定义的权限

如上图所示,协议中定义的slogan属性权限是set , get.而Person实现协议时的属性权限只有get,就会报错.

3.协议中的类型属性,类型方法,类型下标只能用 static 定义,不能使用 class
我们知道classstatic都可以用来定义类型计算属性,类型方法,类型下标.但是如果要在协议中定义类型方法,类型属性只能用static.因为协议可以被类,结构体,枚举遵守.而class只能在类中使用.

  1. 如果要在值类型(结构体 , 枚举)实现的协议方法中修改自身内存,必须在定义协议方法前加上mutating关键字.不能在实现协议方法时添加,那样无效.
  1. 协议中定义的初始化器init,非final类实现协议时必须加上required.
    这样做是为了保证所有的子类都有此初始化器.

协议的继承
协议之间可以彼此继承,一个协议可以继承另一个协议.

协议之间的继承

协议的组合
可以通过不同的协议组合来规范参数类型或者返回值类型,也可以和类组合使用,但最多组合一个类


protocol Study {
    func read()
}

protocol Sport{
    func run()
}

class Person: Sport{
    func run() {
        
    }
    
    func read() {
        
    }
}


func test1(obj: Study){
    //接受遵守Study协议的实例
}

func test2(obj: Study & Sport){
    //接受遵守Study 和 Sport协议的实例
}

func test3(obj: Person & Study & Sport){
    //接受遵守Study 和 Sport协议,并且是Person或者其子类的实例
}


注意: 只能用 & 组合协议,不能使用 | 组合协议,因为使用 | 组合还要单独区分具体是哪个类,这样做毫无意义.

Any , AnyObject

Swift 中提供了两种特殊的类型Any,AnyObject
Any:可以代表任意类型(枚举, 结构体,类,函数类型)
AnyObject:代表任意类类型(也就是使用class创建的任意类型)

例如创建一个可以添加任意类型的数组:

//添加任意类型的数组
var array = Array<Any>()

array.append(1) // Int
array.append("2")// 字符串
array.append(Person())// 类对象
array.append(Season.spring) // 枚举

创建数组还有另一种写法:


var array2 = [Any]()
array2.append("hello")

注意: 如果在协议后面加上 AnyObject 或者 class , 代表只有类类型才能遵守协议

is , as , as? , as!
is: 判断是否为某种类型
as: 用来做强制转换

is 类型判断
as? 类型转换

如果使用as!强制解包,没有值的情况下会直接崩溃,所以我们解包的时候建议还是使用as?

as: 如果能确定百分百转换成功的,可以不加符号,直接使用as,比如:


var num1 = 10 as Float
var num2 = num1 as Any

注意: 任何类型都可以使用 as 转换为 Any 类型,但是不能使用 as 转换回去.必须使用 as? , as! 转换回去.
比如:

X.self. , X.Type , AnyClass(X表示的是类)

如下所示代码:


class Person{
    
}

var p = Person()

它的内存分布如下:


全局变量p存储的是Person实例对象的前8个字节的地址.这8个字节就是Person元类型地址.

X.self:是一个元类型(metadata)的地址,metadata中存放的是类型相关的信息.

也就是说X.self返回的是Person实例对象的前8个字节中存放的内存地址,我们将通过汇编证明这一点.

实例代码:


class Person{
    
}

var p = Person()
var personType = Person.self

以上代码的汇编如下:

Perso.self 汇编分析

从上图可以看到,personType中存放的内容和Person对象的前8个字节的内容相同.

X.selfX.Type类型的:

image.png

所以,也可以这样定义:

元类型的继承
元类型同样也支持继承,父类metatype可以指向子类metatype:

AnyObject.Type: 上面讲过AnyObject是指任意类类型,所以AnyObject.Type表示任意类类型的metadata.

在 Swift 有这样的定义:


public typealias AnyClass = AnyObject.Type

AnyClass也表示任意类类型的metaclass

AnyClass = AnyObject.Type

type(of: <T>):传入一个实例对象,返回这个实例对象的元类型.

type(of:)

type(of:)表面上看起来很像一个函数,但是我们窥探它的汇编发现它的本质就是直接取出实例对象的前8个字节,并没有函数调用:

type(of:) 汇编本质

元类型的应用

一: 通过类名创建控制器实例,类似于 OC 中的 xx.class


    override func viewDidLoad() {
        super.viewDidLoad()

        var vcs = [UIViewController.Type]()
        vcs.append(HomeViewController.self)
        vcs.append(MineViewController.self)
        
        for vc in vcs{
           //通过元类型创建视图控制器
            let childVC = vc.init()
            self.addChild(childVC)
        }
        
    }

二: 通过父类元类型,创建子类实例.注意:父类中的 init() 方法必须加上 required ,因为必须要确保子类都有 init() 方法的实现.


class Person{
    required init(){
        
    }
}

class Student: Person {}
class Teacher: Person {}
class Driver: Person {}

func createPerson(pTypes: [Person.Type]) -> [Person]{
    
    var persons = [Person]()
    for pType in pTypes{
        
        persons.append(pType.init())
    }
    return persons
}

Self:一般用作方法返回值,用来限定方法返回值和方法调用者必须是同一类型,类似OC中的instancetype

如上图所示,testPerson类中的实例方法,返回值是Self.即方法调用者和返回值必须是同一类型.为什么返回Person实例对象报错了呢?因为 Person 可能也会有子类,所以不能写死

所以要这样写:

但是通过元类型创建实例对象必须要加上required:

Self也可表示当前类型:

比如说下面代码:


class Person{
    var age = 0
    static var name = "佚名"
    func makeOneSelf(){
        print("my name is \(Self.name) , age is \(self.age)")
    }
}

Self代表当前类型,也就是Person类型,所以能调用类型属性;
self表示调用方法的当前实例对象.

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