Swift5.0 - day5-继承、初始化、可选链、协议(下)

简介: Swift5.0 - day5-继承、初始化、可选链、协议(下)

三、可选链(Optional Chaining)



  • 如果可选项为nil,调用方法、下标、属性失败,结果为nil
  • 如果可选项不为nil,调用方法、下标、属性成功,结果会被包装成可选项;如果结果本来就是可选项,不会进行再次包装


class Car { var price = 0 }
class Dog { var weight = 0 }
class Person {
    var name: String = ""
    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
var age2 = person?.age() // Int?
var name = person?.name  // String?
var index = person?[6]   // Int?
if let  _ = Person?.eat() { 
      print("eat调用成功")
} esle {
     print("eat调用失败")
}


  • 如果多个?可以链接在一起,如果链中任何一个节点是nil,那么整个链就会调用失败,如下面的 var price = person?.car?.price ,当 personnil的 时候就不会再调用 car?


var dog = person?.dog // Dog?
var weight = person?.dog.weight // Int? 
var price = person?.car?.price // Int?
  • 可选链的使用


var scores = ["Jack": [86, 82, 84], "Rose": [79, 94, 81]]
scores["Jack"]?[0] = 100
scores["Rose"]?[2] += 10
scores["Kate"]?[0] = 88
var num1: Int? = 5
num1? = 10 // Optional(10)
var num2: Int? = nil
num2? = 10 // nil
var dict: [String : (Int, Int) -> Int] = [
    "sum" : (+),
    "difference" : (-)
]
var result = dict["sum"]?(10, 20) // Optional(30), Int?


提示

  • scores["Jack"]为nil的 时候就不会再去调用 [0]
  • num1? = 10 也一样,当num1为nil的时候,就不需要 10 再赋值
  • var dict: [String : (Int, Int) -> Int] 里面的 (+)  和 (-) 代表两个整数的 相加或者相减


四、协议(Protocol)



  • 4.1、协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)


protocol Test1 {}
protocol Test2 {}
protocol Test3 {}
class TestClass : Test1, Test2, Test3 {}
  • 协议中定义方法时不能有默认参数值
  • 默认情况下,协议中定义的内容必须全部都实现(也有办法办到只实现部分内容)


protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}
  • 4.2、协议中的属性


protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}
  • 协议中定义属性时必须用var关键字
  • 实现协议时的属性权限要不小于协议中定义的属性权限
  • 协议定义get、set,用var存储属性或get、set计算属性去实现
  • 协议定义get,用任何属性都可以实现


class Person : Drawable {
     var x: Int = 0
     let y: Int = 0  // 因为是只读 get ,所以用 let 也可以
     func draw() {
        print("Person draw")
     }
     subscript(index: Int) -> Int { 
        set {}
        get { index }
     }
}
class Person : Drawable {
     var x: Int {
         get { 0 }
         set {}
     }
     var y: Int { 0 }
     func draw() { 
         print("Person draw") 
     } 
     subscript(index: Int) -> Int {
         set {}
         get { index } 
     }
}
  • 4.3、static、class
    为了保证通用,协议中必须用static定义类型方法、类型属性、类型下标,因为 class 定义的只能在类里面使用,static定义的在类 、枚举、结构体 里面都可以使用


protocol Drawable {
   static func draw()
}
class Person1 : Drawable {
     class func draw() {
         print("Person1 draw")
     }
}
class Person2 : Drawable {
     static func draw() {
         print("Person2 draw")
     }
}
  • 4.4、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
     }
}
  • 4.5、init
  • 协议中还可以定义初始化器init,非final类实现时必须加上required


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)
    }
}

提示:required 是协议的要求,非final类实现时必须加上required;override 是重写了父类的指定初始化器


  • 4.6、init、init?、init!
  • 协议中定义的init?、init!,可以用init、init?、init!去实现
  • 协议中定义的init,可以用init、init!去实现


protocol Livable {
   init()
   init?(age: Int)
   init!(no: Int)
}
class Person : Livable {
   required init() {}
   // required init!() {}
   required init?(age: Int) {}
   // required init!(age: Int) {}
   // required init(age: Int) {}
   required init!(no: Int) {}
   // required init?(no: Int) {}
   // required init(no: Int) {}
}
  • 4.7、协议的继承
    一个协议可以继承其他协议


protocol Runnable {
    func run()
}
protocol Livable : Runnable {
    func breath()
}
class Person : Livable {
    func breath() {}
    func run() {}
}
  • 4.8、协议组合
    协议组合,可以包含1个类类型(最多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) {}
  • 4.9、CaseIterable
    让枚举遵守 CaseIterable 协议,可以实现遍历枚举值


enum Season : CaseIterable {
    case spring, summer, autumn, winter
}
let seasons = Season.allCases  // 返回数组
print(seasons.count) // 4
for season in seasons {
    print(season)
} // spring summer autumn winter

提示:Season.allCases 等价于 [Season.spring,Season. summer,Season. autumn,Season. winter]


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


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


  • 4.11、Any、AnyObject
  • Swift提供了2种特殊的类型:Any、AnyObject
  • Any:可以代表任意类型(枚举、结构体、类,也包括函数类型)
  • AnyObject:可以代表任意类类型(在协议后面写上: AnyObject代表只有类能遵守这个协议)


在协议后面写上: class也代表只有类能遵守这个协议

var stu: Any = 10
stu = "Jack"
stu = Student()
  • 创建1个能存放任意类型的数组


// var data = Array<Any>() 
var data = [Any]() 
data.append(1) 
data.append(3.14)
data.append(Student())
data.append("Jack") 
data.append({ 10 })
  • 4.12、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")
   }
}
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 = [Any]() data.append(Int("123") as Any)
var d = 10 as Double
print(d) // 10.0
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
  • 4.13、X.self、X.Type、AnyClass
  • X.self 是一个元类型(metadata)的指针,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
public typealias AnyClass = AnyObject.Type 
var anyType2: AnyClass = Person.self 
anyType2 = Student.self
var per = Person()
var perType = type(of: per) // Person.self 
print(Person.self == type(of: per)) // true
  • 4.14、元类型的应用
  • 元类型的应用一


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]))
  • 元类型的应用二


import Foundation
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(Person.self)!) // Swift._SwiftObject


  • 4.15、Self
  • Self 代表当前类型


class Person {
    var age = 1
    static var count = 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()
// Person
print(p.test())
var stu = Student()
// Student
print(stu.test())


目录
相关文章
|
6月前
|
Swift iOS开发
Swift 语言: 什么是协议(Protocol)?如何实现和使用协议?
Swift 语言: 什么是协议(Protocol)?如何实现和使用协议?
194 2
|
算法 Swift C++
34 Swift为了协议 关联类型
Swift为了协议 关联类型
72 0
|
存储 Swift iOS开发
31 Swift 继续聊聊协议扩展
Swift 继续聊聊协议扩展
109 0
|
存储 Java C#
30 如何在Swift中实现继承
如何在Swift中实现继承
56 0
|
16天前
|
存储 Swift 开发者
Swift 协议
Swift 协议
23 0
|
18天前
|
存储 Swift
Swift 继承
10月更文挑战第30天
23 0
|
6月前
|
设计模式 Swift iOS开发
【Swift开发专栏】Swift中的协议与委托模式
【4月更文挑战第30天】Swift编程语言强调协议与委托模式。协议定义了类型需实现的方法和属性,如`SomeProtocol`示例。遵循协议的类、结构体或枚举需实现协议要求。协议可继承,也可作为类型使用。委托模式让对象间通信更灵活,通过协议实现,如`DataSourceDelegate`示例。实战案例展示了在`UITableView`和自定义下载器中使用委托模式。
122 0
|
4月前
|
存储 安全 Swift
Swift高级特性:泛型与协议
【7月更文挑战第10天】Swift高级特性:泛型与协议增强代码复用与类型安全。泛型允许编写通用代码,如`swap`函数和泛型`Stack`结构体,支持类型约束如`Comparable`。协议定义行为蓝图,类型遵循协议需实现其要求。通过两者结合,构建高效灵活的代码结构。
|
6月前
|
安全 Swift 开发者
【Swift开发专栏】Swift类的继承与多态
【4月更文挑战第30天】Swift中的OOP聚焦于类继承与多态,提供代码复用和类型安全。继承通过`class`和冒号实现,子类继承父类属性和方法,支持单继承以降低复杂性。多态借助协议和类型兼容实现,允许统一处理不同类型的对象。继承用于构建复杂类,多态则使代码更通用、可扩展。理解并运用这些概念对Swift开发者至关重要。
57 0
|
Swift C++ Ruby
32 Swift面向协议编程初探
32 Swift面向协议编程初探
71 0