十三、可選鏈、協議、元類型

可選鏈

可選鏈是一個調用和查詢可選屬性、方法和下標的過程,它可能為 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閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容