協議(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 { }
協議中定義方法時不能有默認引數值

默認情況下,協議中定義的內容必須全部都實作
協議中的屬性
協議中定義屬性必須用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

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,代表只有類能遵守這個協議

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

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

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中的地址值就是一開始呼叫的地址

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


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


我們還可以用以下方式來獲取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指標

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/275005.html
標籤:iOS
上一篇:Swift 進階(七)方法、下標
