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版权协议,转载请附上原文出处链接和本声明。