十三、可选链、协议、元类型

可选链

可选链是一个调用和查询可选属性、方法和下标的过程,它可能为 nil

  • 如果可选项是 nil ,属性、方法或者下标的调用,结果为 nil
  • 如果可选项包含值,属性、方法或者下标的调用成功,结果会被包装为可选项
  • 多个?可以链接在一起,如果链中任何一个节点是 nil ,那么整个链就会调用失败。
class Car {
    var price = 0
}

class Dog {
    var weight = 0
}

class Person {
    var name = ""
    var dog: Dog = Dog()
    var car: Car? = Car()
    func age() -> Int{
        18
    }
    func eat() {
        print("Person eat")
    }
    subscript(index: Int) ->Int{
        index
    }
}


var person :Person? = Person()
var age1 = person?.age()//Int? Optional(18)
//强制展开则在可选项为 nil 时触发运行时错误
var age2 = person!.age()//Int 18
var name = person?.name//String? Optional("")
var index = person?[6]//Int? Optional(6)

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

person?.eat()

func getName()->String{
    print("getName:Jack")
    return "jack"
}
//如果person为nil 则不会调用getName()
person?.name = getName()

//多个?可以链在一起
//如果链中任何一个节点为nil 那么整个链调用失败
var dog = person?.dog//Dog?
var weight = person?.dog.weight//Int?
var price = person?.car?.price//Int?

如果结果本来就是可选项,不会进行再次包装

可选链的应用
var scoreArr = ["jack":[86,85,98],
                "rose":[56,64,23]
]
var s = scoreArr["jack"]?[0]//Optional(86)
var num1: Int? = 5
num1? = 10//Optional(10) num1不是nil 则赋值

var num2 :Int? = nil
num2? = 10//nil num2为nil 则不赋值
var dict: [String : (Int,Int) -> Int] = [
    "sum" : (+),
    "difference" : (-)
]
var result = dict["sum"]?(10,20)//Optional(30)

协议(Procotol)

  • 协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)
  • 协议中定义方法时不能有默认参数值
  • 默认情况下,协议中定义的内容必须全部实现(如何只实现部分内容后期补充)
协议中的属性
  1. 协议中定义属性时必须用var关键字
  2. 实现协议时的属性权限要不小于协议中定义的属性权限
  • 协议定义set、get,用var存储属性或用get、set计算属性实现
  • 协议定义get,用任何属性都可以实现(let存储属性或get计算属性)
protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index:Int) ->Int {get set}
}

class Person: Drawable {
    var x:Int = 0
    let y:Int = 0
    func draw() {
        print("Person draw")
    }
    subscript(index: Int) -> Int {
        set{
            
        }
        get{
            index
        }
    }
}

class Animal: Drawable {
    var x: Int{
        set{ }
        get{ 0 }
    }
    var y: Int{0}
    func draw() {
        print("Animal draw")
    }
    subscript(index: Int) -> Int {
        set{ }
        get{ index }
    }
}
static、class关键字

为了保证通用,协议中必须用static定义类型方法、类型属性、类型下标

protocol Drawable {
    static func draw()
}

//在遵循该协议的类的实现中,如果需要可以继承 可以改为class
class Person: Drawable {
    static func draw() {
        print("Person Draw")
    }
}

class Animal: Drawable {
    class func draw() {
        print("Animal Draw")
    }
}
mutating

只有将协议中的实例方法标记为mutating

  • 才允许结构体、枚举的具体实现修改自身内存
  • 类在实现方法时不用加mutating,枚举、结构体才需要加mutating
protocol Drawable {
    mutating func draw()
}

//在类中,如果需要可以继承 可以改为class
class Person: Drawable {
    var age: Int = 0
    
    func draw() {
        print("Person Draw")
        age = 10
    }
}

struct Point: Drawable {
    var x: Int = 0
    mutating func draw() {
        print("Animal Draw")
        x = 10//不加mutating 不可修改自身内存的值
    }
}
init
  • 协议中还可以定义初始化器init
  • final类实现必须加上required
    如果从协议实现的初始化器,刚好是重写了父类的指定初始化器,那这个初始化必须同时加requiredoverride
protocol Drawable {
    init(x: Int,y: Int)
}

class Point: Drawable {
    var x: Int
    var y: Int
    
    required init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

final class Size: Drawable{
    var width: Int
    var height: Int
    init(x: Int, y: Int) {
        self.width = x
        self.height = y
    }
}
protocol Livable {
    init(age: Int)
}

class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
}

//如果加上final 可以去掉required
class Student: Person,Livable{
    required override init(age: Int) {
        super.init(age: age)
    }
}
init、init?、init!的区别
  • 协议中定义的init?、init! ,可以用init、init?、init!去实现
  • 协议中定义的init,可以用init、init!去实现
protocol Livable {
    init(age: Int)
//    init?(idNum:Int)
//    init!(sex:String)
}

class Person :Livable {
    var age: Int = 0
    var idNum: Int = 0
    var sex: String = "men"
    
    required init(age: Int) {
        self.age = age
    }
    //required init!(age: Int) {//编译通过
        //self.age = age
    //}
    
    
    required init?(idNum: Int) {
        self.idNum = idNum
    }
    //required init!(idNum: Int) {//编译通过
            //self.idNum = idNum
        //}
    //required init(idNum: Int) {//编译通过
            //self.idNum = idNum
        //}
    

    required init!(sex: String) {
        self.sex = sex
    }
    //required init!(sex: String) {//编译通过
            //self.sex = sex
        //}
    //required init(sex: String) {//编译通过
            //self.sex = sex
        //}
}
协议的继承

一个协议可以继承其他协议

protocol Livable {
    func live(age: Int) -> String
}

protocol Runnable : Livable {
    func run(step: Int) -> String
}

class Person: Runnable {
    func run(step: Int) -> String {
        "\(step+1000)"
    }
    
    func live(age: Int) -> String {
        "\(age)"
    }
}

var person  = Person()
print(person.live(age: 10),person.run(step: 1000))//输出:10 2000
协议组合

协议组合,可以包含1个类类型

protocol Livable {
}

protocol Runnable {
}

class Person{
}
//接收Person或者其子类的实例
func fn0(obj:Person) { }
//接收遵守Livable协议的实例
func fn1(obj:Livable) { }
//接收同时遵守Livable和Runnable协议的实例
func fn2(obj:Livable & Runnable) { }
//接收同时遵守Livable和Runnable协议并且是Person或者其子类的实例
func fn3(obj:Person & Livable & Runnable) { }
//类型别名
typealias RealPerson = Person & Livable & Runnable
//接收同时遵守Livable和Runnable协议并且是Person或者其子类的实例
func fn4(obj:RealPerson) { }
CaseIterable

让枚举遵守CaseIterable协议,可以实现遍历枚举值

![CaseIterable协议](https://upload-images.jianshu.io/upload_images/7361389-a567ab3e43df54fe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
enum Season :Int, CaseIterable {
    case spring = 0,summer,autumn,winter
}

let seasons = Season.allCases

for season in seasons{
    print(season)
}
/* 输出:
 spring
 summer
 autumn
 winter
 */
CustomStringConvertible

遵守CustomStringConvertibleCustomDebugStringConvertible协议,都可以自定义实例的打印字符串

class Person: CustomStringConvertible,CustomDebugStringConvertible {
    var age = 0
    var description: String {
        "person_\(age)"
    }
    
    var debugDescription: String {
        "debugperson_\(age)"
    }
}

var person = Person()
print(person)//输出:person_0
debugPrint(person)//输出:debugperson_0
po 调用的是debugDescription

print 调用的是CustomStringConvertible协议的description
debugPrint、po指令 调用的是CustomDebugStringConvertible协议的debugDescription

Any 、 AnyObject

Swift提供了2中特殊的类型:Any 、 AnyObject

  • Any:可以代表任意类型(枚举、结构体、类以及函数类型)
  • AnyObject:可以代表任意类型(协议后面写上:AnyObject代表只有类可以遵守这个协议)

协议后面写上class也代表只有类可以遵守这个协议

class Student {}

var stu: Any = 10
stu = "jack"
stu = Student()

var data = [Any]()
data.append("123")
data.append(123)
data.append(123.23)
data.append(Student())
data.append({10})//闭包表达式
is、as?、as!、as
  • is用来判断是否为某种类型
  • as用来做强制类型转换
protocol Runnable {
    func run()
}

class Person {
}

class Student: Person,Runnable {
    func run() {
       print("Student run")
    }
    
    func study() {
        print("Student study")
    }
}

//is用于判断
var stu:Any = 10
print(stu is Int)//true

stu = "jack"
print(stu is String)//true

stu = Student()
print(stu is Person)//true
print(stu is Student)//true
print(stu is Runnable)//true

//as用于强制类型转换
var stu2:Any = 10
(stu2 as? Student)?.study()//没有调用study()  前?为可能转换失败 后?为可选链

stu2 = Student()
(stu2 as? Student)?.study()//调用study()
(stu2 as? Student)!.study()//调用study() !为强制解包
(stu2 as! Student).study()//调用study() !为强制类型转换
(stu2 as! Runnable).run()//调用run()

元类型(metadata)

  • X.self是一个元类型的指针,metadata存放着类型相关信息
  • X.self属于X.Type类型(与X类型完全不同)
X.self、 X.Type 、AnyClass
class Person {}

class Student: Person {}

var perType: Person.Type = Person.self
var stuType: Student.Type = Student.self
perType = Student.self//编译通过

var anyType: AnyObject.Type = Person.self
anyType = Student.self

public typealias AnyClass = AnyObject.Type
var anyType2: AnyClass = Person.self
anyType2 = Student.self

var per = Person()
perType = type(of: per)//Person.self
print(Person.self == type(of: per))//输出:true
元类型的应用
class Animal {
    required init() {
        
    }
}

class Cat: Animal { }
class Dog: Animal { }
class Pig: Animal { }

func create(_ classes:[Animal.Type]) -> [Animal] {
    var arr = [Animal]()
    for classType in classes {
        arr.append(classType.init())
    }
    return arr
}

print(create([Cat.self,Dog.self,Pig.self]))

print(class_getInstanceSize(Cat.self))//输出:16
print(class_getSuperclass(Dog.self)!)//输出:Animal
print(class_getSuperclass(Animal.self))//输出:Optional(Swift._SwiftObject)

从结果可以看的出来,Swift还有一个隐藏基类Swift._SwiftObject
Swift runtime源码查看

Self
  • Self代表当前类型
  • Self一般用作返回值类型,限定返回值跟方法调用者必须一致(也可用作参数类型)
//Self代表当前类型
class Animal {
    var age: Int = 1
    static var count: Int = 2

    func run() {
        print(self.age)//输出:1
        print(Self.count)//输出:2
    }
}
//Self一般用作返回值类型
protocol Runnable {
    func test() -> Self
}

class Person : Runnable{
    required init() {
        
    }
    func test() -> Self {
        type(of: self).init()
    }
}

class Student: Person {
}

var p = Person()
print(p.test())//Person

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

推荐阅读更多精彩内容