主頁 > 移動端開發 > Swift 進階(九)協議、元型別

Swift 進階(九)協議、元型別

2021-04-12 06:26:42 移動端開發

協議(Protocol)

基本概念

協議可以用來定義方法、屬性、下標的宣告,協議可以被結構體、類、列舉遵守

protocol Drawable {
    func draw()
    var x: Int { get set } // get和set只是宣告
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}

多個協議之間用逗號隔開

protocol Test1 { }
protocol Test2 { }
protocol Test3 { }

class TestClass: Test1, Test2, Test3 { }

協議中定義方法時不能有默認引數值

-w633

默認情況下,協議中定義的內容必須全部都實作

協議中的屬性

協議中定義屬性必須用var關鍵字

實作協議時的屬性權限要不小于協議中定義的屬性權限

  • 協議定義get、set,用var存盤屬性或get、set計算屬性去實作
  • 協議定義get,用任何屬性都可以實作
protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}

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

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

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

static、class

為了保證通用,協議中必須用static定義型別方法、型別屬性、型別下標

protocol Drawable {
    static func draw()
}

class Person1: Drawable {
    static func draw() {
        print("Person1 draw")
    }
}

class Person2: Drawable {
    class func draw() {
        print("Person2 draw")
    }
}

mutating

只有將協議中的實體方法標記為mutating,才允許結構體、列舉的具體實作修改自身記憶體

類在實作方法時不用加mutating,結構體、列舉才需要加mutating

protocol Drawable {
    mutating func draw()
}

class Size: Drawable {
    var width: Int = 0
    
    func draw() {
        width = 10
    }
}

struct Point: Drawable {
    var x: Int = 0
    mutating func draw() {
        x = 10
    }
}

init

協議中還可以定義初始化器init,非final類實作時必須加上required

目的是為了讓所有遵守這個協議的類都擁有初始化器,所以加上required強制子類必須實作,除非是加上final不需要子類的類

protocol Drawable {
    init(x: Int, y: Int)
}

class Point: Drawable {
    required init(x: Int, y: Int) {
        
    }
}

final class Size: Drawable {
    init(x: Int, y: Int) {
        
    }
}

如果從協議實作的初始化器,剛好是重寫了父類的指定初始化器,那么這個初始化必須同時加上required、override

protocol Livable {
    init(age: Int)
}

class Person {
    init(age: Int) { }
}

class Student: Person, Livable {
    required override init(age: Int) {
        super.init(age: age)
    }
}

協議中定義的init?、init!,可以用init、init?、init!去實作

protocol Livable {
    init()
    init?(age: Int)
    init!(no: Int)
}

class Person1: Livable {
    required init() {
        
    }
    
    required init?(age: Int) {
        
    }
    
    required init!(no: Int) {
        
    }
}

class Person2: Livable {
    required init() {
        
    }
    
    required init!(age: Int) {
        
    }
    
    required init?(no: Int) {
        
    }
}

class Person3: Livable {
    required init() {
        
    }
    
    required init(age: Int) {
        
    }
    
    required init(no: Int) {
        
    }
}

協議中定義的init,可以用init、init!去實作

protocol Livable {
    init()
    init?(age: Int)
    init!(no: Int)
}

class Person4: Livable {
    required init!() {
        
    }
    
    required init?(age: Int) {
        
    }
    
    required init!(no: Int) {
        
    }
}

協議的繼承

一個協議可以繼承其他協議

protocol Runnable {
    func run()
}

protocol Livable: Runnable {
    func breath()
}

class Person: Livable {
    func breath() {
        
    }
    
    func run() {
        
    }
}

協議組合

協議組合可以包含一個型別別

protocol Runnable { }
protocol Livable { }
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
func fn4(obj: RealPerson) { }

CaseIterable

讓列舉遵守CaseIterable協議,可以實作遍歷列舉值

enum Season: CaseIterable {
    case spring, summer, autumn, winter
}

let seasons = Season.allCases
print(seasons.count)

for season in seasons {
    print(season)
} // spring, summer, autumn, winter

CustomStringConvertible

遵守CustomStringConvertible、CustomDebugStringConvertible協議,都可以自定義實體的列印字串

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

var person = Person()
print(person) // person_0
debugPrint(person) // debug_person_0

print呼叫的是CustomStringConvertible協議的description

debugPrint、po呼叫的是CustomDebugStringConvertible協議的debugDescription

-w529

Any、AnyObject

Swift提供了兩種特殊的型別Any、AnyObject

Any可以代表任意型別(列舉、結構體、類,也包括函式型別)

var stu: Any = 10
stu = "Jack"
stu = Size()
var data = https://www.cnblogs.com/funkyRay/p/[Any]()
data.append(1)
data.append(3.14)
data.append(Size())
data.append("Jack")
data.append({ 10 })

AnyObject可以代表任意型別別

在協議后面寫上: AnyObject,代表只有類能遵守這個協議

-w644

在協議后面寫上: class,也代表只有類能遵守這個協議

-w642

is、as

is用來判斷是否為某種型別

protocol Runnable {
    func run()
}

class Person { }

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

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用來做強制型別轉換

protocol Runnable {
    func run()
}

class Person { }

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

var stu: Any = 10
(stu as? Student)?.study() // 沒有呼叫study

stu = Student()
(stu as? Student)?.study() // Student study
(stu as! Student).study() // Student study
(stu as? Runnable)?.run() // Student run
var data = https://www.cnblogs.com/funkyRay/p/[Any]()
data.append(Int("123") as Any)

var d = 10 as Double
print(d) // 10.0

元型別

X.self

X.self是一個元型別的指標,metadata存放著型別相關資訊

X.self屬于X.Type型別

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

var per = Person()
perType = type(of: per)
print(Person.self == type(of: per)) // true

AnyClass的本質就是AnyObject.Type

-w492

var anyType2: AnyClass = Person.self
anyType2 = Student.self

元型別的應用

class Animal {
    required init() {
        
    }
}

class Cat: Animal {
    
}

class Dog: Animal {
    
}

class Pig: Animal {
    
}

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

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

// a1、a2、a3、a4的寫法等價
var a1 = Animal()
var t = Animal.self
var a2 = t.init()
var a3 = Animal.self.init()
var a4 = Animal.self()

Self

Self代表當前型別

class Person {
    var age = 1
    static var count = 2
    
    func run() {
        print(self.age)
        print(Self.count)
    }
}

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()) // test_enum.Person

var stu = Student()
print(stu.test()) // test_enum.Student

元型別的本質

我們可以通過反匯編來查看元型別的實作是怎樣的

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

我們發現最后存盤到全域變數pType中的地址值就是一開始呼叫的地址

-w1031

再通過列印,我們發現pType的值就是Person實體物件的前8個位元組的地址值,也就是類資訊

-w1031
-w1032

我們再來看下面的示例代碼

var p = Person()
var pType = type(of: p)

通過分析我們可以看到type(of: p)本質不是函式呼叫,只是將Person實體物件的前8個位元組存盤到pType中,也證明了元型別的本質就是存盤的類資訊

-w1031
-w1030

我們還可以用以下方式來獲取Swift的隱藏基類_TtCs12_SwiftObject

class Person {
    var age: Int = 0
}

class Student: Person {
    var no: Int = 0
}

print(class_getInstanceSize(Student.self)) // 32
print(class_getSuperclass(Student.self)!) // Person
print(class_getSuperclass(Student.self)!) // _TtCs12_SwiftObject
print(class_getSuperclass(NSObject.self)) // nil

我們可以查看Swift原始碼來分析該型別

發現SwiftObject里面也有一個isa指標

-w686

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/275005.html

標籤:iOS

上一篇:Swift 進階(七)方法、下標

下一篇:Swift 進階(十)錯誤處理、泛型

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more