可選鏈
可選鏈是一個調用和查詢可選屬性、方法和下標的過程,它可能為 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)
- 協議可以用來定義方法、屬性、下標的聲明,協議可以被枚舉、結構體、類遵守(多個協議之間用逗號隔開)
- 協議中定義方法時不能有默認參數值
- 默認情況下,協議中定義的內容必須全部實現(如何只實現部分內容后期補充)
協議中的屬性
- 協議中定義屬性時必須用var關鍵字
- 實現協議時的屬性權限要不小于協議中定義的屬性權限
- 協議定義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
如果從協議實現的初始化器,剛好是重寫了父類的指定初始化器,那這個初始化必須同時加required、override
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協議,可以實現遍歷枚舉值

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
遵守CustomStringConvertible、CustomDebugStringConvertible協議,都可以自定義實例的打印字符串
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