Swift-语法基础

Swift 编译流程

Swift -> Swift AST -> Swiftc -> Swift IL -> LLVM IR -> LLVM compiler -> ARM(x86,other)

Swift Code -> Swift AST(语法树) -> Raw Swift IL(中间代码) -> Canonical Swift IL(中间代码) -> LLVM IR -> Assembly(汇编代码) -> Executable(二进制代码)

swiftc
swiftc存放在Xcode内部
Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

生成语法树:swiftc -dump-ast main.swift
生成最简洁的SIL代码:swiftc -emit-sil main.swift
生成LLVM IR代码:swiftc -emit-ir main.swift -o main.ll
生成汇编代码:swiftc -emit-assembly main.swift -o main.s

一.数据类型

1.1 常见数据类型

  • 值类型(value type)
    • 枚举(enum):optional
    • 结构体(struct):Bool、Int、Float、Double、Character、String、Array、Dictionary、Set
  • 引用类型(指针类型) (reference type)
    • 类(class)

整数类型:int8 int16 int32 int64 uint8…
在32bit平台int等价于int32,占32个字节,在64bit平台等价int64占64个字节
整数最值:UInt8.max、Int16.min
print(Int32.min)//-2147483648

1.2 常量、变量

/**
 *常量只能赋值一次,不要求在编译时期赋值,使用前赋值即可
 *常量、变量在初始化之前,都不能使用
 */
let age1 = 10
let age2 : Int
age2 = 10

var num = 10
num += 20
num += 30

/**
 错误示例
 let age
 age = 20
 
 错误示例
 常量、变量在初始化之前,都不能使用
 let age : Int
 var height : Int
 Print(age)
 print(height)
 */

1.3 标识符

/**
 *标识符(即为:常量名、变量名、函数名)几乎可以使用任何字符
 *标识符不能以数字开头,不能包含空白字符、制表符、箭头等特殊字符
 */
let ? = "ET"
func ??(){
    print("666")
}
??()

1.4 字面量

//字面量
let bool = true
let string = "xiaomage"
let character:Character = "a"

//整数
let intDecimal = 17//十进制
let intBinary = 0b10001//二进制 以0b开头
let intOctal = 0o21//八进制 以0o开头
let intHexadecimal = 0x11//十六进制 以0x开头

//浮点数
let doubleDecimal = 125.0//十进制 ,等价于1.25e2;    0.0125等价于1.25e-2
let doubleHexadecimal1 = 0xFp2 //十六进制,意味着15*2^2,相当于60.0
let doubleHexadecimal2 = 0xFp-2 //十六进制,意味着15*2^-2,相当于十进制的3.75

//类型转换
let int = 3
let dou = 0.14159
let pi = Double(int) + dou
//字面量可直接相加,因为数字字面量本身没有明确的类型
let result = 3 + 0.14159

//数组
let array = [1,2,3,4,5]
//字典
let dictionary = ["age":13,"height":180]

//元组(tuple)
let error = (404,"Not Found")
error.0
error.1
let (code,message) = error
print("The status code is \(message)")
let(justCode,_) = error
let http200Status = (statusCode:200,discription:"ok")
print("The status code is \(http200Status.statusCode)")

二.流程控制

2.1 if-else

//条件可以省略括号,条件后面语句的大括号不能省略
var age = 24
if age >= 22 {
    print(age)
}else if age > 23 {
    print("big boy")
}

2.2 while

while age < 10 {
    age -= 1
}
//repeat while
var num = -1
repeat {
    print(num)//打印了1次,相当于 do-while
}while num > 0
//repeat while  ==  do while
//这里不用num--,swift3开始,去除了自增(++)、自减(--)运算符

2.3 for

let names = ["anna","alex","brian","jack"]
//0123
for i in 0...3 {
    print(names[i])
}
//
for name in names[..<2] {
    print(name)
}

/**
 * 区间运算符
 * a...b 等价于 a>= <=b
 * [2...] 单向区间 2,3,4......;如果控制数组,不会大于数组个数-1
 * [..<2]....-1,0,1,2;如果控制数组,不会小于0
 * 字符,字符串也是使用区间运算符,但默认不能用于for in
 */
let range:ClosedRange<String> = "cc"..."ff"
if range .contains("cd") {
    print(range)
}

//带间隔的区间值
let hours = 11
let hourInerval = 2
//tickMark的取值;从4开始,累加2,不超过11
for tickMark in stride(from: 4, through: hours, by: hourInerval) {
    print(tickMark)
}//4 6 8 10

2.4 switch

/**
 * 1.case,default后面不能写大括号{}
 * 2.默认可以不写break,并不会贯穿到后面的条件
 * 3.使用fallthrough 可以实现贯穿效果
 * 4.switch必须要保证能处理所有的情况
 * 5.case,default后面至少要有一条语句
 * 6.如果不想做任何事,加个break即可
 * 7.如果能保证处理所有情况,也可以不必使用default(比如枚举)
 * 8.switch也支持character,string
 */
 
//case、default 后面不能写大括号{}
var num = 1
switch num {
case 1:
    print("num is 1")
    break
case 2:
    print("num is 2")
    break
default:
    print("num is other")
    break
}

//默认可以不写break,并不会贯穿到后面的条件
var num2 = 1
switch num2 {
case 1:
    print("num2 is 1")
case 2:
    print("num2 is 2")
default:
    print("num2 is other")
}

//fallthrough
//使用fallthrough可以实现贯穿效果
var number = 1
switch number {
case 1:
    print(number)
    fallthrough
case 2:
    print(number)
default:
    break
}
//number is 1
//number is 2

//如果能保证处理所有情况,也可以不必使用default(比如枚举)
enum Answer {case right,wrong}
let answer = Answer.right
switch answer {
case .right:
    print(answer)
case .wrong:
    print(answer)
}

//多个条件判断 用逗号隔开
switch answer {
case .right,.wrong:
    print(answer)
}

//区间匹配,元组匹配
let point = (1,1)
switch point {
case (0,0):
    print("the origin")
case(-2...2,-2...2):
    print("inside of box")
default:
    print("outside of box")
}

//值绑定
//where 条件过滤
let p = (10,-10)
switch p {
case let (x,y) where x == y:
    print(x)
case let (x,y) where x == -y:
    print(y)
default:
    break
}
//将所有正数加起来 where过滤,作用相当于continue
var numbers = [10,20,-10,-20,50]
var sum = 0
for num in numbers where num>0 {
    sum = sum + num;
    print(sum)
}

//标签语句 内层循环中的判断语句控制外层循环
outer : for i in 1...4 {
    for j in 1...4 {
        if j == 3 {
            continue outer
        }
        if i == 3 {
            break outer
        }
        print(i)
    }
}

三.函数

func pi() -> Double {
    return 3.14
}


//函数的文档注释

/// 求和【概述】
///
/// - Parameter v1:第1个参数
/// - Parameter v2:第2个参数
/// - Returns:2个整数的和
///
/// - note:传入2个整数即可【批注】
///
//func sum (v1:Int,v2:Int) -> Int {
//    v1+v2
//}
//sum(v1:10,v2:20)


//参数标签
func goToWork (at time:String) {
    print(time)
}
goToWork(at:"10点")


//默认参数
/**
 * c++的默认参数值有个限制:必须从右往左设置
 * swift 不需要必须从右往左设置,是因为有参数标签
 * 默认 name,job
 */
func check(name:String = "nobody",age:Int,job:String = "none"){
    print(name,age,job)
}
check(age: 12)


//可变函数 ...
//紧跟可变参数后面的参数不能带下划线
func sum(_ numbers :Int...) -> Int {
    var total = 0
    for number in numbers {
        total += number
    }
    return total
}
sum(10,20,30)

//print 函数,_ items: Any..., separator: String = " ", terminator: String = "\n"




//inout 输入输出参数
/**
 * 可变参数不能标记为inout
 * inout 参数的本质是地址传递(引用传递)
 * inout 参数只能传入可以被多次赋值的
 */

/**
 * 汇编验证 inout 参数的本质是地址传递(引用传递)
 * callq 调用函数指令
 * leaq 地址传递指令
 */

var number = 10
func add(_ num :inout Int) {
    num = 20
}
add(&number)
print(number)




//函数重载 function overload
/**
 * 函数名相同
 * 参数个数不同||参数类型不同||参数标签不同
 *
 * 1.返回值类型与函数重载无关
 * 2.默认参数值和函数重载一起使用产生二义性,编译器并不会报错(在c++中会报错)
 * 3.可变参数,省略参数函数标签,函数重载一起使用产生二义性时,编译器有可能会报错
 *
 */
func sums(v1:Int,v2:Int) -> Int {
    v1+v2
}
func sums(v1:Int,v2:Int,v3:Int) -> Int{
    v1+v2+v3
}
sums(v1: 1, v2: 2, v3: 3)


//内联函数(Inline Function)
/**
 * 如果开启了编译器优化(Release模式默认会开启优化),编译器会自动将某些函数变成内联函数
 * 将函数调用展开成函数体
 *
 * 哪些函数不会被内联
 * 1.函数体比较长
 * 2.包含递归调用
 * 3.包含动态派发(相当于父类指针指向子类)
 * ......
 */

//关键字 @inline
//永远不会被内联(即使开启了编译器优化)
@inline(never) func inlineTest() {
    print("inlinTest")
}
//开启编译器优化情况下,即使代码很长,也会被内联(递归调用函数、动态派发的函数除外)
@inline(__always) func inlineTest2() {
    print("inlinTest2")
}



//函数类型(Function Type)
/**
 * 每一个函数都是有类型的,函数类型由形式参数类型、返回值类型组成
 */
func test () {} // ()->Void 或者 ()->()
func sum(a:Int,b:Int)->Int{
    a+b
}//(Int,Int)->Int

//定义变量
var fn:(Int,Int)->Int = sum
fn(1,2)//调用时不需要参数标签


//函数类型作为函数参数
func difference(v1:Int,v2:Int)->Int {
    v1-v2
}
func printResult(_ mathFn:(Int,Int)->Int,_ a:Int,_ b:Int) {
    print("result:\(mathFn(a,b))")
}

//函数类型作为函数返回值
//返回值是函数类型的函数,叫做 高价函数 (Higher-Order Function)
func next(_ input:Int) -> Int {
    input+1
}
func previous(_ input:Int) -> Int {
    input-1
}
func forwoard(_ forward:Bool) -> (Int)->Int {
    forward ? next : previous
}
forwoard(true)(3)
forwoard(false)(3)

//关键字 typealias 用来给类型起别名
//void就是空元组 typealias void = ()
typealias Byte = Int8
typealias Date = (year:Int,month:Int,day:Int)
func testDate (_ date:Date){
    print(date.year,date.month,date.day)
}
testDate((2021,3,10))


//嵌套函数 存储在代码区
func forward2(_ forward:Bool) -> (Int) -> Int {
    func next (_ input:Int) ->Int {
        input+1
    }
    func previous (_ input:Int) -> Int {
        input-1
    }
    return forward ? next : previous
}
forward2(true)(5)
forward2(false)(5)

四.枚举&内存分析

//枚举

//枚举的基本用法
enum Direction {
    case north
    case south
    case east
    case west
}
enum Direction2 {
    case north,south,east,west
}

var dir = Direction.west
dir = .north
print(dir)//north

switch dir {
case .north:
    print(dir)
case .south:
    print(dir)
case .east:
    print(dir)
case .west:
    print(dir)
}


//关联值 Associated Values
//有时会将枚举的成员值跟其他类型的关联存储在一起,会非常有用
enum Score {
    case point(Int)
    case grate(Character)
}
var score = Score.point(96)
score = .grate("A")
print(score)

switch score {
case let .point(i):
    print(i,"points")
case let .grate(i):
    print("grade",i)
}


enum Date {
    case digit(year:Int,month:Int,day:Int)
    case string(String)
}

var date = Date.digit(year: 2011, month: 9, day: 10)
date = .string("2021-01-10")
switch date {
case .digit(let year,let month,let day):
    print(year,month,day)
case let .string(value):
    print(value)
}


enum Password {
    case number(Int,Int,Int,Int)
    case gesture(String)
}

var pwd = Password.number(3, 1, 4, 5)
pwd = .gesture("123456")



//原始值(Raw Values)
//枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做:原始值
enum PokerSuit:Character {
    case spade = "A"
    case heart = "B"
    case diamond = "C"
    case club = "D"
}

var suit = PokerSuit.spade
print(suit)
print(suit.rawValue)
print(PokerSuit.club.rawValue)


//隐式原始值(Implicitly Assigned Raw Values)
//如果枚举的原始值类型是Int、String, Swift会自动分配原始值

enum Direction3:String {
    case north,south,east,west
}
print(Direction3.north.rawValue)
print(Direction3.south.rawValue)

enum season : Int {
    case spring,summer,autumn,winter
    
}
print(season.spring.rawValue)//0
print(season.summer.rawValue)//1
print(season.autumn.rawValue)//2
print(season.winter.rawValue)//3



//递归枚举 (Recursive Enumeration)
indirect enum ArithExpr {
    case number(Int)
    case sum(ArithExpr,ArithExpr)
    case difference(ArithExpr,ArithExpr)
}

//enum ArithExpr {
//    case number(Int)
//    indirect case sum(ArithExpr,ArithExpr)
//    indirect case difference(ArithExpr,ArithExpr)
//}


let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let two = ArithExpr.number(2)

let sum = ArithExpr.sum(five, four)
let difference = ArithExpr.difference(sum, two)

func calculate (_ expr:ArithExpr) -> Int {
    switch expr {
    case let .number(value):
        return value
    case let .sum(left, right):
        return calculate(left) + calculate(right)
    case let .difference(left, right):
        return calculate(left) - calculate(right)
    }
}

calculate(difference)


//占用内存 4x8+1 = 33 个字节  关联值
//enum Password {
//    case number(Int,Int,Int,Int)
//    case gesture(String)
//}
MemoryLayout<Password>.size

//占用内存 1 个字节   有原始值
enum Season : Int {
    case spring = 1,summer,autumn,winter
}
MemoryLayout<Season>.size





//枚举内存分析


enum TestEnum {
    case test
}
var t = TestEnum.test //没有内存

print(MemoryLayout<TestEnum>.size)//0 没有内存
print(MemoryLayout<TestEnum>.stride)//1
print(MemoryLayout<TestEnum>.alignment)//1





enum TestEnum {
    case test,test2
}
var t = TestEnum.test

print(MemoryLayout<TestEnum>.size)//1
print(MemoryLayout<TestEnum>.stride)//1
print(MemoryLayout<TestEnum>.alignment)//1




/**
注意:原始值不占用枚举变量的内存
*/
enum TestEnum {
    case test1(Int,Int,Int)//24
    case test2(Int,Int)//16
    case test3(Int)//8
    case test4(Bool)//1
    case test5
}

//1个字段存储成员值(表达是哪个成员)
//N个字节存储关联值(N取占用内存最大的关联值),任何一个case的关联值都共用这N个字节
//共用体

var e = TestEnum.test1(1,2,3)
print(Mems.ptr(ofVal: &e))

//var t = 10
//t = 20

//大小端问题  小端模式:高高低低 读出来是这样:0x00 00 00 00 00 00 00 01
//01 00 00 00 00 00 00 00
//02 00 00 00 00 00 00 00
//03 00 00 00 00 00 00 00
//00 存储成员值,标识
//00 00 00 00 00 00 00

print(MemoryLayout<TestEnum>.size)
print(MemoryLayout<TestEnum>.stride)
print(MemoryLayout<TestEnum>.alignment)

//0x000000010000c320
//25
//32
//8

e = TestEnum.test2(4, 10)
//04 00 00 00 00 00 00 00
//0A 00 00 00 00 00 00 00
//00 00 00 00 00 00 00 00
//01 00 00 00 00 00 00 00
e = .test3(6)
//06 00 00 00 00 00 00 00
//00 00 00 00 00 00 00 00
//00 00 00 00 00 00 00 00
//02 00 00 00 00 00 00 00
e = .test4(true)
//01 00 00 00 00 00 00 00
//00 00 00 00 00 00 00 00
//00 00 00 00 00 00 00 00
//03 00 00 00 00 00 00 00
e = .test5
//00 00 00 00 00 00 00 00
//00 00 00 00 00 00 00 00
//00 00 00 00 00 00 00 00
//04 00 00 00 00 00 00 00

五.可选项

//可选项
//一般也叫可选类型,它允许将值设置为nil
var name : String? = "jack"
name = nil
print(name)
//nil

var age : Int?//默认初始值为nil
age = 10
age = nil


//强制解包
//可选项是堆其他类型的一层包装,可以将它理解为一个盒子
//如果为nil,那么它是一个空盒子
//如果不为nil,那么盒子里装的是:被包装类型的数据
var age : Int?//默认初始值为nil
age = 10
var num = age! + 10
print(age)
//Optional(10)

//使用示例 字符串转换int
let number = Int("q123")
if number != nil {
    print(number!)
}else{
    print("转换失败")
}
//转换失败


//可选项绑定 
//如果可选项有值将自动解包
if let number = Int("123") {
    print(number)
}else{
    print("字符串转换整数失败,解包失败")
}
//123


//等价写法 多个可选项判断需要用逗号隔开,不能直接&&连接
if let a = Int("1") {
    if let b = Int("40") {
        if a < b && b < 100 {
            print(a,b)
        }
    }
}

if let a = Int("1") ,
   let b = Int("40") ,
   a < b && b < 100 {
    print(a,b)
}
//1 40
//1 40


//空合并运算符 ?? (Nil-Coalescing Operator)
//a ?? b
//a是可选项
//b是可选项 或者 不是可选项
//b跟a存储的类型必须相同
//如果a不为nil,就返回a
//如果a为nil,就返回b
//如果b不是可选项,返回a时将会自动解包
var a : Int?
var b = 10
let c = a ?? b
print(c)
//10

//??配合if let使用
let a = Int("1")
let b = Int("40")
if let c = a ?? b {
    print(c)//1
}
//类似于 if a != nil || b != nil

if let c = a ,let d = b {
    print(c,d)//1 40
}
//类似于  if a != nil && b!= nil


//guard 语句
//guard 条件 else {
//    //do something...
//    退出当前作用域
//    //return、break、continue、throw、error
//}

//当guard语句的添加为false时,就会执行大括号里面的代码
//当guard语句的条件为true时,就会跳过guard语句
//guard语句特别适合用来“提前退出”
//当使用guard语句进行可选项绑定时,绑定的常量(let)、变量(var)也能再外层作用域中使用

func methodTest() {
    var age : Int?
    guard let b = age else {
        print(age ?? 100)
        return
    }
    print(b)
}
methodTest() //100


//隐式解包
//在确定某个可选类型一定有值时,就可以在其后面加个感叹号!定义一个隐式解包的可选项
let num1: Int! = 10
let num2: Int = num1//隐式解包的可选项其实偷偷进行了强制解包
print(num2)//10


//字符串插值
//可选项再字符串插值或者直接打印时,编译器会发出警告
var age: Int? = 10
print("My age is \(age)")//My age is Optional(10)
//至少3种方法消除警告
print("My age is \(age!)")//My age is 10
print("My age is \(age ?? 0)")//My age is 10
print("My age is \(String(describing: age))")//My age is 10


//多重可选项
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
print(num2 == num3)//ture

var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
print(num2 == num3)//false
print((num2 ?? 1) ?? 2)//2
print((num3 ?? 1) ?? 2)//1
以上两种结果不同,num3直接=10时会被??包装两层,num3直接=nil时只会包装一层

六.结构体、类

//区别:结构体为值类型,类为引用类型
//结构体
//自定义初始化,查看汇编与系统完全相同
func testStruct() {
    struct Point {
        var x : Int = 0
        var y : Int = 0

//        var x : Int
//        var y : Int
//        init() {
//            x = 0
//            y = 0
//        }
    }
    var p = Point()

    print(MemoryLayout<Point>.size)//16
    print(MemoryLayout<Point>.stride)//16
    print(MemoryLayout<Point>.alignment)//8

    print(Mems.ptr(ofVal: &p))
}
testStruct()


func testStruct2() {
    struct Point2 {
        var x : Int?
        var y : Int?
        init(x:Int,y:Int) {
            self.x = x
            self.y = y
        }
        func structMethod(v1:Int, v2:Int) -> Int {
        	return v1 + v2
    	}
    }
    var p = Point2(x:10,y:10)
}
testStruct2()

//类
class Size {
    var width: Int
    var height: Int
    init(width: Int,height: Int) {
        self.width = width
        self.height = height
    }
    func structMethod(v1:Int, v2:Int) -> Int {
        return v1 + v2
    }
}



/**
 
 向堆空间申请内存的汇编代码
 
 Class __allocating_init
 libswiftCore.dylib: swift_allocObject
 libswiftCore.dylib: swift_slowAlloc
 libsystem_malloc.dylib: malloc
 在Mac、iOS中的malloc函数分配的内存大小总数是16的倍数
 通过class_getInstanceSize可以得知类的对象真正使用的内存大小
 
 */


/**
 赋值操作
 
 值类型
 值类型赋值给var、let或者给函数传参,是直接将所有内存拷贝一份
 类似于对于文件的copy,paste操作,产生了全新的文件副本,属于深拷贝(deep copy)
 
 在Swift标准库中,为了提升性能,string,array,dictionary,set采用了copy on write
 比如仅当有"写"操作时,才会真正执行拷贝操作
 对于标准库值类型的赋值操作是,Swift能确保最佳性能,所以没必要为了保证最佳性能来避免赋值
 建议:不需要修改的,尽量定义成let
 
 引用类型
 引用赋值给var、let或者给函数传参,是将内存地址拷贝一份
 类似于制作一个文件的替身(快捷方式、链接),指向的是同一个文件。属于浅拷贝(shallow copy)
 
 */

/**
 汇编规律
 
 内存地址格式为:0x4bdc(%rip),一般是全局变量
 内存地址格式为:-0x78(%rbp),一般为局部变量
 内存地址格式为:0x10(%rax),一般是堆空间
 
 rip是下一条汇编代码前面的那个地址,写死的
 rbp是外层函数的地址值,变的
 
 */

//栈空间分配,从高到低


//下面用以汇编分析实例
//结构体与类的内存布局

func testVlaueType() {
    class Size {
        var width = 1
        var height = 2
    }
    
    struct Point {
        var x = 3
        var y = 4
    }
    
    var size = Size()
//    var point = Point()

}
testVlaueType()



func testRefrenceType() {
    class Size{
        var width: Int
        var height: Int
        init(width: Int,height: Int) {
            self.width = width
            self.height = height
        }
    }
    var s1 = Size(width: 10, height: 20)
    var s2 = s1
    s2.width = 11
    s2.height = 22
}
testRefrenceType()

七.闭包

//在swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数
/**
 闭包表达式
 {
     (参数列表) -> 返回值类型 in
     函数体代码
 }
 */

func sum(_ v1:Int,_ v2:Int) -> Int{
    v1+v2
}

var fn = {
    (v1:Int,v2:Int) -> Int in
    return v1+v2
}
fn(10,20)

//{
//    (v1:Int,v2:Int) -> Int in
//    return v1+v2
//}(10,20)

//闭包表达式的简写
func exec(v1:Int,v2:Int,fn:(Int,Int)->Int) {
    print(fn(v1,v2))
}

exec(v1: 10, v2: 20, fn: {
    (v1:Int, v2:Int) -> Int in
    return v1 + v2
})
exec(v1: 10, v2: 20, fn: {
    v1, v2 in
    return v1 + v2
})
exec(v1: 10, v2: 20, fn: {
    v1, v2 in v1 + v2
})
exec(v1: 10, v2: 20, fn: {
    $0 + $1
})

exec(v1: 10, v2: 20, fn: +)

/**
 尾随闭包
 如果将一个很长的闭包表达式作为函数的最后一个实参,使用尾随闭包可以增强函数的可读性
 尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式
 */
exec(v1: 10, v2: 20) {
    (v1, v2) -> Int in
    return v1 + v2
}
exec(v1: 10, v2: 20) {
    v1, v2 in
    return v1 + v2
}
exec(v1: 10, v2: 20) {
    v1, v2 in v1 + v2
}
exec(v1: 10, v2: 20) {
    $0 + $1
}

//如果闭包表达式是函数的唯一实参,而且使用了尾随闭包的语法,那就不需要在函数名后边写圆括号
func exec2(fn:(Int,Int) -> Int) {
    print(fn(1,2))
}
exec2(fn: {$0 + $1})
exec2() {$0 + $1}
exec2 {$0 + $1}


//示例 - 数组的排序
//func sort(by areInIncreasingOrder:(Element,Element) -> Bool)

//返回true:i1排在i2前面
//返回false:i1排在i2后面
func cmp(i1:Int, i2:Int) -> Bool {
    //大的排在前面
    return i1 > i2
}

var nums = [11,2,18,6,5,68,45]
nums.sort(by: cmp)
print(nums)


nums.sort(by: {
    (i1:Int, i2:Int) -> Bool in
    return i1 > i2
})
nums.sort(by: {
    i1, i2 in
    return i1 < i2
})
nums.sort(by: {
    i1, i2 in i1 < i2
})
nums.sort(by: {
    $0 < $1
})
nums.sort(by: <)
nums.sort() {
    $0 < $1
}
nums.sort {
    $0 > $1
}


//如果返回值是函数类型,那么参数的修饰要保持统一  如:下面函数里外的inout需要一致
func add(_ num: Int) -> (inout Int) -> Void {
    func plus(v: inout Int) {
        v += num
    }
    return plus
}
var num = 5
add(20)(&num)
print(num)


//自动闭包
//如果第一个数大于0,返回第一个数,否则返回第二个数
//func getFirstPositive(_ v1:Int, _ v2:Int) -> Int {
//    return v1 > 0 ? v1 : v2
//}
//getFirstPositive(10, 20)
//getFirstPositive(-2, 20)
//getFirstPositive(0, -4)

//改变函数类型的参数,可以让v2延迟加载
func getFirstPositive(_ v1:Int, _ v2:() -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(-4){20}

func getFirstPositive(_ v1:Int,_ v2: @autoclosure ()->Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(-4, 20)

/**
 @autoclosure 会自动将20封装成闭包{20}
 @autoclosure 只支持 ()-> T 格式的参数
 @autoclosure 并非只支持最后一个参数
 空合并运算符 ?? 使用 @autoclosure 技术
 有 @autoclosure、无 @autoclosure,构成了函数重载
 */

//为了避免与期望冲突,使用了 @autoclosure 的地方最好明确注释清楚:这个值会被推迟执行

八.属性

/**
 属性
 
 swift中跟实例相关的属性可以分为2大类
 1.存储属性(Stored Property)
 类似于成员变量这个概念
 存储在实例的内存中
 结构体,类可以定义存储属性
 枚举不可以定义存储属性
 
 2.计算属性(Computed Property)
 本质就是方法(函数)
 不占用实例的内存
 枚举,结构体,类都可以定义计算属性
 */

struct Circle {
    //存储属性
    var raduis: Double
    //计算属性
    var diameter: Double {
        set {
            raduis = newValue/2
        }
        get {
            raduis * 2
        }
    }
}


var c = Circle(raduis: 10)
c.raduis = 11
print(c.diameter)

c.diameter = 40
print(c.raduis)



print(MemoryLayout<Double>.stride)//8
print(MemoryLayout<Double>.size)//8

print(MemoryLayout<Circle>.stride)//8
print(MemoryLayout<Circle>.size)//8



/**
 存储属性
 
 关于存储属性,swift有个明确的规定
 在创建类或者结构体的实例是,必须为所有的存储属性设置一个合适的初始值
 
 可以在初始化器里为存储属性设置一个初始值
 可以分配一个默认的属性值作为属性定义的一部分
 
 */

struct Point {
    var x:Int
    var y:Int
    init() {
        x = 10
        y = 20
    }
}
var p = Point()

struct point {
    var x:Int = 10
    var y:Int = 20
}
var p2 = point()





/**
 计算属性
 
 set传入的新值默认叫做newValue,也可以自定义
 定义计算属性只能用var,不能用let
 let代表常量,值是一成不变的
 
 只读计算属性,只有get,没有set
 
 */

//读写计算属性
struct Circle {
    var radius:Double
    var diameter:Double {
        set(newDiameter){
            radius = newDiameter / 2
        }
        get {
            radius * 2
        }
    }
}
var c = Circle(radius: 20)
c.diameter = 22

//只读计算属性:只有get,没有set
struct Circle2 {
    var radius : Double
    var diameter : Double {
        get {
            radius * 2
        }
    }
}
var c2 = Circle2(radius: 20)
print(c2.diameter)
//40.0


/**
 枚举rawValue原理
 
 枚举原始值rawValue的本质是:只读计算属性
 */

//重新实现 只读计算属性 rawValue
enum TestEnum:Int {
    case test1 = 1,test2 = 2,test3 = 3
    var rawValue: Int {
        switch self {
        case .test1:
            return 10
        case .test2:
            return 11
        case .test3:
            return 12
        }
    }
}
print(TestEnum.test2.rawValue)
//11


/**
 延迟存储属性(Lazy Stored Property)
 
 使用lazy可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化
 
 lazy属性必须是var,不能是let
 let必须在实例的初始化方法完成之前就拥有值
 
 如果多条线程同时第一次访问lazy属性
 无法保证属性只被初始化1次
 
 */

class Car {
    init() {
        print("car init")
    }
    func run() {
        print("car running")
    }
}
class Person {
    lazy var car = Car()
    init() {
        print("person init")
    }
    func goOut() {
        car.run()
    }
}

let p = Person()
print("-----------")
p.goOut()

//person init
//-----------
//car init
//car running



实例,获取图片
//class PhotoView {
//    lazy var image:Image = {
//        let url = "https://www.baidu.com/xx.png"
//        let data = Data(url:url)
//        return Image(data:data)
//    }()
//}
//var photo = PhotoView()
//photo.image

//存储属性 func定义函数 解析上面闭包写法
//func getImage() -> Image {
//    let url = "https://www.baidu.com/xx.png"
//    let data = Data(url:url)
//    return Image(data:data)
//}
//class PhotoView {
//    lazy var image:Image = getImage()
//}




/**
 延迟存储属性注意点
 
 当结构体包含一个延迟存储属性时,只有 var 才能访问延迟存储属性
 因为延迟属性初始化时需要改变结构体的内存
 
 */


struct point {
    var x:Int = 0
    var y:Int = 0
    lazy var z = 0
}
let p2 = point()
//print(p2.z) Cannot use mutating getter on immutable value: 'p2' is a 'let' constant


/**
 属性观察器 Property Observer
 
 可以为非lazy的var存储属性设置属性观察器(1.非lazy,2.var,3.存储属性)
 
 willSet会传递新值,默认叫newValue
 didSet会传递旧值,默认叫oldValue
 在初始化器中设置属性值不会触发willSet和didSet
 在属性定义时设置初始值也不会触发willSet和didSet
 
 */

struct Circle {
    var radius:Double {
        willSet{
            print("willSet",newValue)
        }
        didSet{
            print("didSet",oldValue,radius)
        }
    }
    init() {
        self.radius = 1.0
        print("Circle init")
    }
}

//Circle init
var cir = Circle()

//willSet 10.5
//didSet 1.0 10.5
cir.radius = 10.5

//10.5
print(cir.radius)



/**
 全局变量、局部变量
 属性观察器、计算属性的功能,同样可以应用在全局变量、局部变量身上
 */
var num : Int {
    get{
        return 10
    }
    set{
        print("setNum",newValue)
    }
}
num = 11 //setNum 11
print(num)//10


var num2 : Int = 1 {
    willSet{
        print("willSet",newValue)
    }
    didSet{
        print("didSet",oldValue,num2)
    }
}
num2 = 9
//willSet 9
//didSet 1 9


func test() {
    var age = 10 {
        willSet{
            print("willSet",newValue)
        }
        didSet{
            print("didSet",oldValue,age)
        }
    }
    age = 11
    //willSet 11
    //didSet 10 11
}
test()






/**
 inout
 
 inout的本质总结
 
 1、如果实参有物理内存地址,且没有设置属性观察器
 直接将实参的内存地址传入函数(实参进行引用传递)
 
 2、如果实参是 计算属性 或者 设置了属性观察器
 采用了Copy In Copy Out的做法
 a、调用该函数时,先复制实参的值,产生副本【get】
 b、将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值
 c、函数返回后,再将副本的值覆盖实参的值【set】
 
 3、总结:inout的本质就是引用传递(地址传递)
 */

struct shape {
    var width:Int
    var side:Int {
        willSet{
            print("willSet side ",newValue)
        }
        didSet{
            print("didset side ",oldValue,side)
        }
    }
    var girth:Int {
        set {
            width = newValue/side
            print("willset girth ",newValue)
        }
        get {
            print("get girth ")
            return width * side
        }
    }
    func show() {
        print("width=\(width),side=\(side),girth=\(girth)")
    }
}

func test(_ num:inout Int) {
    num = 20
}

//var age = 10
//test(&age)
//print(age)//20

var s = shape(width: 10, side: 4)
test(&s.girth)
s.show()
//get girth
//willset girth  20
//get girth
//width=5,side=4,girth=20





/**
 类型属性 (Type Property)
 严格来说,属性可以分为:  实例属性,类型属性
 
 实例属性(Instance Property):只能通过实例去访问
 a、存储实例属性(Stored Instance Property):存储在实例的内存中,每个实例都有1份
 b、计算实例属性(Computed Instance Property)
 
 类型属性(Type Property):只能通过类型去访问
 a、存储类型属性(Stored Type Property):整个程序运行过程中,就只有1份内存(类似于全局变量)
 b、计算类型属性 (Computed Type Property )
 
 可以通过static定义类型属性
 如果是类,也可以用关键字class
 
 
 
 类型属性细节
 1、不同于存储实例属性,你必须给存储类型属性设定初始值
 因为类型没有像实例那样的init初始化器来初始化存储属性
 
 2、存储类型属性默认就是lazy,会再第一次使用的时候才初始化
 就算被多个线程同时访问,保证只会初始化一次
 3、存储类型属性可以是let
 
 枚举类型也可以定义类型属性(存储类型属性、计算类型属性)
 */

struct Shape {
    var width:Int = 0 //实例属性
    static var count:Int = 0 //类型属性,只有一份内存
    
    static var count2:Int { //计算类型属性
        return 10
    }
}




struct Car2 {
    static var count: Int = 0 //类型属性
    init() {
        Car2.count += 1
    }
}
let c1 = Car2()
let c2 = Car2()
let c3 = Car2()
print(Car2.count)//3





/**
 单例模式 
 存储类型属性默认就是lazy,会再第一次使用的时候才初始化
 就算被多个线程同时访问,保证只会初始化一次
 */
public class FileManager {
    public static let shared = FileManager()
    private init() {
    }
    func open() {
    }
    func close() {
    }
}
//调用
FileManager.shared.open()

九.方法&下标

//方法(Method)
/**
 * 枚举、结构体、类都可以定义实例方法、类型方法
 * 1、实例方法(Instance Method):通过实例调用
 * 2、类型方法(Type Method):通过类型调用,用static或者class关键字定义
 */


class car {
    static var count = 0
    init() {
        car.count += 1
    }
    //类型方法
    static func getCount () -> Int {
        self.count
    }
}

/**
 * self
 * 在实例方法中代表实例
 * 在类型方法中代表类型
 *
 * 在列下方法static function getCount中
 *  count 等价于self.count、Car.self.count、Car.count
 */






/**
 * mutating  语法糖
 * 结构体和枚举是值类型,默认情况下,值类型的属性不能被自身的实例方法修改
 * 在func关键字前加mutating可以允许这种修改行为
 */

struct Point {
    var x = 1.0,y = 1.0
    
    mutating func moveBy(deltaX:Double,deltaY:Double) {
        x += deltaX
        y += deltaY
    }
}

enum StateSwitch {
    case low, middle, heigh
    mutating func next() {
        switch self {
        case .low:
            self = .middle
        case .middle:
            self = .heigh
        case .heigh:
            self = .low
        }
    }
}

/**
 * @discardableResult  语法糖
 * 在func前面加个@discardableResult,可以消除:函数调用后返回值未被使用的警告
 */

@discardableResult func get() -> Int {
    return 10
}
get()


/**
 * 下标(subscript)
 * 使用subscript可以给任意类型(枚举、结构体、类)添加下标功能,有些地方翻译为:下标脚本
 * subscript的语法类似于实例方法、计算属性,本质就是方法(函数)
 */

class Point2 {
    var x = 0.0,y = 0.0
    subscript (index:Int) -> Double{
        set {
            if index == 0 {
               x = newValue
            }else if index == 1 {
                y = newValue
            }
        }
        get {
            if index == 0 {
                return x
            }else if index == 1 {
                return y
            }
            return 0
        }
    }
}
var p = Point2()
p[0] = 11.1
p[1] = 22.2
print(p.x)
print(p.y)
print(p[0])
print(p[1])

/**
 * subscript 中定义的返回值类型决定了
 * get方法的返回值类型
 * set方法中newValue的类型
 *
 * subscript可以接受多个参数,并且类型任意
 *
 * subscript 可以没有set方法,但必须有get方法
 *
 * 可以设置参数标签
 */

//可以设置参数标签
class Point3 {
    var x = 0.0,y = 0.0
    subscript (index i:Int) -> Double{
        set {
            if i == 0 {
               x = newValue
            }else if i == 1 {
                y = newValue
            }
        }
        get {
            if i == 0 {
                return x
            }else if i == 1 {
                return y
            }
            return 0
        }
    }
}
var p3 = Point3()
print(p3[index: 0])

//多个参数 类型方法
class Sum {
    static subscript(v1:Int,v2:Int) -> Int{
        return v1+v2
    }
}
print(Sum[10,20])

//结构体、类作为返回值对比
//结构体 值类型拷贝一份,没有set不能直接修改
struct Point4 {
    var x = 0,y=0
}

class pointManager {
    var point = Point4()
    subscript (index:Int) -> Point4 {
        set{point = newValue}
        get{point}
    }
}
var pm = pointManager()
pm[0].x = 11
pm[0] = Point4(x:11,y:pm[0].y)


//类 指针引用,指针不变,没有set也可以修改
class Point5 {
    var x = 0,y=0
}

class pointManager2 {
    var point = Point5()
    subscript (index:Int) -> Point5 {
        get{point}
    }
}
var pm2 = pointManager2()
pm2[0].x = 11
pm2[0].y = 22


//接收多个参数的下标
class Grid {
    var data = [[0,1,2],[3,4,5],[6,7,8]]
    subscript (row:Int,column:Int) -> Int {
        set{
            guard row >= 0 && row<3 && column>=0 && column<3 else{
                return
            }
            data[row][column] = newValue
        }
        get{
            guard row >= 0 && row<3 && column>=0 && column<3 else{
                return 0
            }
            return data[row][column]
        }
    }
}
var grid = Grid()
grid[0,1] = 77
grid[1,2] = 88
print(grid[0,1])

十.继承


/**
 * 继承(Inheritance)
 * 值类型(枚举、结构体)不支持继承,只有类支持继承
 *
 * 没有父类的类,称为:基类
 * Swift并没有像OC、Java那样的规定:任何类最终都要继承自某个基类
 *
 * 子类可以重写父类的下标、方法、属性,重写必须加上override关键字
 *
 * 重写类型方法、下标
 * 被class修饰的类型方法、下标,允许被子类重写
 * 被static修饰的类型方法、下标,不允许被子类重写
 */

class Animal {
    var age = 1
    
    func speak() {
        print("Animal speak")
    }
    subscript(index: Int) -> Int {
        return index
    }
}

class Dog: Animal {
    var weight = 30
}

class SmailDog: Dog {
    var iq = 0
    
    override var age :Int {
        set{
            weight = newValue / 2
        }
        get{
            return weight * 2
        }
    }
    
    override func speak() {
        print("SmailDog speak")
    }
    override subscript(index: Int) -> Int {
        if index == 0 {
            print(index)
        }else {
            print(index * index)
        }
        return index
    }
}

let a = SmailDog()
print(a.age, a.weight, a[1])


/**
* 重写类型属性
* 被class修饰的计算类型属性,可以被子类重写
* 被static修饰的类型属性(存储、计算),不可以被子类重写
*/

class Circle {
    var Perimeter: Int = 100
    static var radius2: Int = 20
    class var diameter: Int {
        set {
            radius2 = newValue / 2
        }
        get {
            return radius2 * 2
        }
    }
}
class SubCircle: Circle {
    override static var diameter: Int {
        set {
            radius2 = newValue / 4
        }
        get {
            return radius2 * 4
        }
    }
}

SubCircle.radius2 = 400
print(SubCircle.diameter)//1600
SubCircle.diameter = 400
print(SubCircle.radius2)//100
var cir = SubCircle()

/**
 * 属性观察器
 * 可以在子类中为父类属性(除了只读计算属性、let属性)增加属性观察器
 *
 */
class Circle {
    var radius = 10 {
        willSet{
            print(newValue)//20     30
        }
        didSet{
            print(oldValue,radius)//10 20    10 30
        }
    }
}
let cir = Circle()
cir.radius = 20

class SubCircle: Circle {
    override var radius: Int {
        willSet{
            print(newValue)//30
        }
        didSet{
            print(oldValue,radius)//10 30
        }
    }
}
let subCir = SubCircle()
subCir.radius = 30

十一.初始化

/**
 * 初始化器
 *
 * 类、结构体、枚举都可以定义初始化器
 * 类有2种初始化器:指定初始化器(designated initializer)、便捷初始化器(convenience initializer)
 *
 *
 * 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器
 * 默认初始化器总是类的指定初始化器
 * 类偏向于少量指定初始化器,一个类通常只有一个指定初始化器
 *
 *
 * 初始化器的相互调用规则
 * 指定初始化器必须从它的直系父类调用指定初始化器
 * 便捷初始化器必须从相同的类里调用另一个初始化器
 * 便捷初始化器最终必须调用一个指定初始化器
 *
 * 这一套规则保证了
 * 使用任意初始化器,都可以完整地初始化实例
 */

class Animal {
    //指定初始化器
//    init(parameters) {
//        statements
//    }
    //便捷初始化器
//    convenience init(parameters) {
//        statements
//    }
}


/**
 * 两段式初始化
 *
 * Swift在编码安全方面是煞费苦心,为了保证初始化过程的安全,设定了两段式初始化、 安全检查
 *
 * 两段式初始化
 *
 * 第1阶段:初始化所有存储属性
 * 1 外层调用指定\便捷初始化器
 * 2 分配内存给实例,但未初始化
 * 3 指定初始化器确保当前类定义的储存属性都初始化
 * 4 指定初始化器调用父类的初始化器,不断向上调用,形成初始化器链
 *
 * 第2阶段:设置新的存储属性值
 * 1 从顶部初始化器往下,链中的每一个指定初始化器都有机会进一步定制实例
 * 2 初始化器现在能够使用self(访问、修改它的属性,调用它的实例方法等等)
 * 3 最终,链中任何便捷初始化器都有机会定制实例以及使用self
 *
 *
 * 安全检查
 * 指定初始化器必须保证在调用父类初始化器之前,其所在类定义的所有存储属性都要初始化完成
 * 指定初始化器必须先调用父类初始化器,然后才能为继承的属性设置新值
 * 便捷初始化器必须先调用同类中的其它初始化器,然后再为任意属性设置新值
 * 初始化器在第1阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用self
 * 直到第1阶段结束,实例才算完全合法
 *
 *
 * 重写
 * 当重写父类的指定初始化器时,必须加上override(即使子类的实现是便捷初始化器)
 * 如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上override
 * 因为父类的便捷初始化器永远不会通过子类直接调用,因此,严格来说,子类无法重写父类的便捷初始化器
 *
 *
 * 自动继承
 * 1 如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器
 * 2 如果子类提供了父类所有指定初始化器的实现(要么通过方式1继承,要么重写)
 *  子类自动继承所有的父类便捷初始化器
 * 3 就算子类添加了更多的便捷初始化器,这些规则仍然适用
 * 4 子类以便便捷初始化器的形式重写父类的指定初始化器,也可以作为满足规则2的一部分
 */


/**
 * required
 * 用required修饰指定初始化器,表明其所有子类都必须实现该初始化器(通过继承或者重写实现)
 * 如果子类重写了required初始化器,也必须加上required,不用加override
 */

class Person {
    var age : Int = 10
    
//    required init(age:Int) {
//        self.age = age
//    }
    
//    init(age:Int) {
//        self.age = age
//    }
    
    convenience init(age: Int) {
        self.init()
        self.age = age
    }
}

class Student: Person {
//    required init(age: Int) {
//        super.init(age: age)
//    }
    
//    override init(age: Int) {
//        super.init(age: age)
//    }
    
    convenience init(age: Int) {
        self.init()
        self.age = age
    }
}

/**
 * 可失败初始化器
 * 类、结构体、枚举都可以使用init?定义可失败初始化器
 *
 * 不允许同时定义参数标签、参数个数、参数类型相同的可失败初始化器和非可失败初始化器
 * 可以用init!定义隐式解包的可失败初始化器
 * 可失败初始化器可以调用非可失败初始化器,非可失败初始化器调用可失败初始化器需要进行解包
 * 如果初始化器调用一个可失败初始化器导致初始化失败,那么整个初始化过程都失败,并且之后的代码都停止执行
 * 可以用一个非可失败初始化器重写一个可失败初始化器,但反过来是不行的
 */

class Person {
    var name : String
    init?(name:String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}

/**
 * 反初始化器(deinit)
 * deinit叫做反初始化器,类似于C++的析构函数、OC中的dealloc方法
 * 当类的实例对象被释放内存时,就会调用实例对象的deinit方法
 *
 * deinit不接受任何参数,不能写小括号,不能自行调用
 * 父类的deinit能被子类继承
 * 子类的deinit实现执行完毕后会调用父类的deinit
 */

class Student: Person {
    deinit {
        print("Student对象销毁了")
    }
}

十二.可选链

/**
 * 可选链(Optional Chaining)
 * 如果可选项为nil,调用方法、下标、属性失败,结果为nil
 * 如果可选项不为nil,调用方法、下标、属性成功,结果会被包装成可选项
 * 如果结果本来就是可选项,不会进行再次包装
 *
 * 多个?可以链接在一起
 * 如果链中任何一个节点是nil,那么整个链就会调用失败
 */
class Car {
    var price = 0
}
class Dog {
    var weight = 0
}
class Person {
    var name: String = ""
    var car: Car? = Car()
    var dog = Dog()
    func age() -> Int {
        18
    }
    func eat() {
        print("Person eat")
    }
    subscript(index: Int) -> Int {
        index
    }
}

var person :Person? = Person()
var price = person?.car?.price//int?

print(price)//Optional(0)

十三.协议

/**
 * 协议(Protocol)
 * 协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)
 *
 * 协议中定义方法时不能有默认参数值
 * 默认情况下,协议中定义的内容必须全部都实现
 * 也有办法办到只实现部分内容
 */
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计算属性去实现 p 协议定义get,用任何属性都可以实现
 */
class Person2 : Drawable {
    var x: Int {
        get { 0 }
        set {}
    }
    var y: Int { 0 }
    func draw() {
        print("Person draw")
    }
    subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}

/**
 * static、class
 * 为了保证通用,协议中必须用static定义类型方法、类型属性、类型下标
 */
protocol Runable {
    static func run()
}

class Person: Runable {
    static func run() {
        print("penson run")
    }
}

/**
 * mutating
 * 只有将协议中的实例方法标记为mutating
 * 才允许结构体、枚举的具体实现修改自身内存
 * 类在实现方法时不用加mutating,枚举、结构体才需要加mutating
 */
protocol Drawable2 {
    mutating func draw()
}
class Size : Drawable2 {
    var width: Int = 0
    func draw() {
        width = 10
    }
}
struct Point : Drawable2 {
    var x: Int = 0
    mutating func draw() {
        x = 10
    }
}
enum SizeEnum : Int,Drawable2   {
    case SizeEnum1 = 1
    case SizeEnum2 = 2
    mutating func draw() {
    }
}

/**
 * init
 * 协议中还可以定义初始化器init
 * 非final类实现时必须加上required
 *
 * 如果从协议实现的初始化器,刚好是重写了父类的指定初始化器
 * 那么这个初始化必须同时加required、override
 *
 */
protocol Runnable {
    init(age:Int)
}
class Person3 {
    var age: Int
    init(age: Int) {
        self.age = age
    }
}
class Children: Person3,Runnable {
    required override init(age: Int) {
        super.init(age: age)
        self.age = age
    }
}

/**
 * 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) {}
}


/**
 * 协议的继承
 * 一个协议可以继承其他协议
 */

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

/**
 * 协议组合
 * 协议组合,可以包含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) {}

/**
 * 协议 CaseIterable
 * 让枚举遵守CaseIterable协议,可以实现遍历枚举值
 */

enum Season : CaseIterable {
    case spring, summer, autumn, winter
}
let list = Season.allCases
print(list.count)
for enumCase: Season in list {
    print(enumCase)
}
//4 spring summer autumn winter

/**
 * CustomStringConvertible
 * 遵守CustomStringConvertible、 CustomDebugStringConvertible协议,都可以自定义实例的打印字符串
 * print调用的是CustomStringConvertible协议的description
 * debugPrint、po调用的是CustomDebugStringConvertible协议的debugDescription
 */

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


/**
 * Any、AnyObject
 * n 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 })


/**
 * 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
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


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

/**
 * 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


/**
 * 元类型的应用
 */
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

//从结果可以看得出来,Swift还有个隐藏的基类:Swift._SwiftObject
//可以参考Swift源码:https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.h

十四.错误处理


十五.泛型


十六.String&Array


十七.高级运算符


十八.扩展


十九.访问控制


二十.内存管理


二十一.字面量


二十二.模式匹配


二十三.从OC到Swift


二十四.函数式编程


二十五.面向协议编程


二十六.响应式编程


二十七.标准库源码分析


二十八.项目实战


Swift 5.0 -> iOS 13
SwiftUI -> iOS 13

版权声明:本文为weixin_42580612原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。