sizeof 不是函数,在汇编里面,没有掉callq
类型分类.png
@inline
内联-1.png 内联-2.png 内联-3.png@inout
inout-1.png- 传的是内存地址(引用传递)
空合并运算符 ??
空运算符-1.png 空运算符-2.pngguard语句
guard-1.png隐式解包
隐式解包-1.pngenum
enum TestEnum{
case test1(Int,Int,Int)
case test2(Int,Int)
case test3(Int)
case test4(Bool)
case test5
}
var e = TestEnum(10,20,30);
- 上面最后一句代码,就是把10、20、30放在e的内存地址上
mov $0xa, 0x4eb(%rip), // rip存储的地址就是 0x4bdf
leaq 0x4bdf(%rip)
- rip存储的是指令的地址
- CPU要执 行的下一条指令地址就存储在rip中
结构体和类区别
- 结构体(值类型,枚举也是值类型),类引用类型
class Size{
var width = 1;
var height = 2;
}
var size = Size();
size是指针,放在内存的栈空间,占8个字节
size指针变量地址,在堆空间,Size对象的内存地址 ,占32个字节,看堆空间的绿色一共4个,4*8=32
1.png
图中蓝色和绿色 内存数据那块,一格表示8个字节。
对象堆空间申请内存过程
闭包
闭包.png-
fn
里面前8个字节存储的是getFn
里面plus
函数地址,后面8个字节存储的是堆空间的地址。 -
fn(1)
,1这个参数在汇编里面是通过rdi寄存器
传进来的,一直存储在rdi寄存器
里,到了plus函数里,直接从rdi寄存器
拿出来就行。 -
num
是存储在堆空间里面的,向堆空间申请了24个字节,前8个字节跟类一样,指向类型信息,再8个字节,放引用计数,最后8个字节,放num。 -
在
plus
里面可以拿到堆空间地址,再调用plus
函数的时候,传了2个参数,一个是参数i
,一个是堆空间地址值
-
num
是在调用getFn
函数时,函数返回时,把num
传到堆空间里的 -
调用一次
getFn
函数,就会开辟一次堆空间
-
plus
没有捕获外部变量 - 上图中
fn1
占16个字节,前面8个字节,放的是plus
地址,后面8个字节是0
-
fn1
里num
在堆空间里的值是14
,汇编看是捕获了2次,第一次是11,第二次是14。
全局变量
- 上面代码
fn1
是函数plus
地址,前面8个字节是plus
地址,后8个字节是0 - 全局变量,不会发生捕获,不算闭包
闭包
:函数+捕获的变量。
-
上面代码调用一个
getFns
只会开辟一次堆空间,这个意思是num1
和num2
各自只开辟一次堆空间,不是说调用plus的时候开辟一次,调用minus的时候开辟一次。 -
num1
只alloc了一次,开辟了一次堆空间,前8个字节存储类型,中间8个字节存储引用计数,最后8个字节存储num1
-
num2
也只alloc了一次,开辟了一次堆空间,前8个字节存储类型,中间8个字节存储引用计数,最后8个字节存储num2
-
num2
和num1
是分开来存储的,
自动闭包
自动闭包.png ??运算符.png属性
属性-1.png 存储属性.png计算属性
枚举
枚举.png
枚举变量占一个字节来存储对应的case。
那么原始值存储在哪?
-
原始值不存储,是个只读的计算属性。
枚举的rawValue.png
延迟存储属性lazy
属性观察器
inout在各个属性之间的地址取值
-
引用传递
代码-1.png
存储属性
test(&s.width)
- 上面代码
width
是存储属性,有地址值,存储在s里面,所以test函数结束到的是s的地址值,又因为width
是s的第一个属性,所以传s的地址值是一样的。
计算属性
test(&s.girth)
- 上面
girth
是计算属性,计算属性本身没有地址值,首先
,调用get方法
拿到返回值,放到局部变量
里面去,第二
,把局部变量的地址值
传给test函数,test函数把20赋值给局部变量
,最后
把局部变量赋值给
girth,调用
set函数`
属性观察器
test(&s.side)
-
side
是属性观察器,先调用test函数
,在test函数
里面进行赋值的时候,会调用set函数
,在set函数
里会触发willSet
和didSet
函数。
传值过程 - 调用test之前,先把side里面的内容赋值给
局部变量
,
- 调用test之前,先把side里面的内容赋值给
- 将
局部变量
的地址值赋值给test函数
,test函数
修改完局部变量
的值
- 将
- 将
局部变量
的值,传递到set方法
里, -
willSet
的newValue就是局部变量的值
- 将
局部变量的值
放到set的存储属性里面 - 赋值完之后调用
didSet
- 真正修改
side
的值,是在test函数
调用完毕之后,才去改的。 -
test函数
是一个独立的函数,只接收一个地址值,修改这个地址值里面的数据。
inout总结
inout总结.png类型属性
类型属性.png 类型属性-count.png- count只有一份内存
类型属性细节
类型属性细节.png单例模式
- 类型属性的最好应用
class Car{
static var count = 1;
}
Car.count = 11;
- count是lazy属性,是在第一次调用的时候才初始化,初始化的时候调用了
swift_once
函数,swift_once
函数里调用了dispatch_once
函数,就等价于 - count是
存储类属性
,线程安全,调用dispatch_once
dispatch_once({
Car.count = 1;
})
-
存储类属性
是全局变量,只不过编译器限制了他的访问量
方法
方法.png self.pngmutating
discardableResult
下标--subscript
下标细节
结构体和类作为下标返回值区别
- struct Point
pm[0].x = 11;
等价于
pm[0].x = Point(x:11,y:pm[0].y);
pm[0].x = 掉的是set方法
- class Point
pm[0].x = 11;
- 相当于掉了下标里面的
get方法
,把point返回,point是一个指针变量
,你可以拿到指针变量,去修改这个地址的值了。所以不报错
, - 要是换成
struct
的Point
,下标函数里没有set方法
,pm[0].x = 11;
就会报错,因为值类型,内容是拷贝赋值给一个新的临时变量,改不了旧的。
接收多个参数的下标
重写类型方法、下标
重写类型方法、下标.png 1.png 2.png- SubCircle里面依然有8个字节来存储radius属性,要不然赋值没地方存。
var subCircle = SubCircle()
subCircle.radius = 10;
打印的结果为
SubCircle getRaius
Circle getRaius
SubCircle setRaius
从父类继承过来的存储属性,都是有存储空间的。不管要不要写成计算属性
- SubCircle里radius属性,get方法里return super.raius 改成raius,直接死循环了,因为不知道拿谁的raius
- 计算属性是有get、set方法
重写实例属性
重写属性.png重写类型属性
重写类型属性.png-
存储属性本身不让被class修饰
不让class修饰.png
属性观察器
-
父类有属性观察器
父类有属性观察器.png -
子类可以给父类的计算属性添加属性观察器
计算属性.png
打印的结果为 Circle getRadius 是 oldValue,因为在赋值之前,要拿到对应的值
final
结构体不存在继承,所以不存在重写,在编译阶段就把函数地址、属性写死了。
初始化器
- 调用规则 从它的直系父类调用指定的初始化器,这是为了保证父类初始化里面一系列操作可以正常执行。
初始化器相互调用
2段式初始化
安全检查
安全检查.png 1.png 2.png
重写
-
便捷初始化器不能被子类调用。
重写.png
- 子类无法去覆盖Person类的
convenience init()
方法的,convenience init()
只能在Person类里其他调用,子类掉用不了,不算重写
自动继承
自动继承.png 第一条规则的解释.png 第二条规则的解释.pngrequired
属性观察器
可失败初始化器
反初始化器 deinit
可选链 Opitional Chaining
协议 Protocol
static class
- static不可以子类重写,class子类可以重写
mutating
mutating.png
init
init.png
- 上面的Student 里面required 来自协议,override来自父类
init 、init?、init!
CaseIterable
-
allCases
返回一个数组
CustomStringConvertible CustomDebugStringConvertible
-
CustomStringConvertible
里面有description
CustomStringConvertible.png
Any AnyObject
**is as? as! as **
is as? as! as.png
X.self、X.Type、AnyClass
- X是类
Person.self 放元类地址
跟person对象的前8个字节存储的地址一样
1.png
元类型的使用
继承
继承2.png
Self
-
跟OC里面的instanceType差不多
Self.png
error 错误类型
开发错误.png自定义错误
自定义错误-1.png
do-catch
do-catch.png
处理Error
try? try!
-
try?
异常是nil,不异常是Option值 -
try!
告诉系统一定没错, 如果发生错误, 程序会崩溃. 不推荐使用 - 首先要明白抛出异常后异常的运动:异常被抛出后,中断整个处理,异常不断向外层(范围)传递,直到遇到catch代码块群,会与catch代码块的条件进行匹配,匹配符合则进入此代码块处理。如果遇到没有条件的catch{}那么直接在这个代码里处理。如果抛出的异常一直到最外层仍没有被catch{}处理,那么程序会卡住(后面的处理全部中断)
rethrows
defer
- 一段代码,不管你是正常执行完,还是直接崩溃跳出代码,之后,需要做的操作,比如读数据库,不管是正常读完,关闭数据库,还是SQL写的不对直接崩溃,退出,关闭数据库,在
defer
执行。
泛型
泛型-1.png泛型属性
泛型属性.png关联类型(Associated Type)
关联类型.png类型约束
类型约束-1.png 类型约束-2.png equal.png-
equal
函数的意思是:S1、S2必须遵守Stackable
协议,S1、S2的关联对象
必须一致,S1的关联对象Element
必须遵守Hashable协议
,多要求的话必须在返回值后面加where关键字
协议类型的注意点
泛型解决一
泛型解决二---不透明类型(Opaque Type)
-
不透明类型使用场景:
返回一个遵守某种协议的对象,拿给别人用,但是又不告诉别人具体是那种类型,而且别人拿到的接口仅仅是协议里面的接口,用不透明类型
解决2-some.png
assert断言
-
do-catch
不可以捕获
fatalError
访问控制Access Control
- 实体 ,下面
name
就是实体
class Person{
public var name = ""
}
模块.png
-
open
其他模块可以继承、重写
open class Person{
open var name = ""
}
-
internal
只能在当前实体里使用,不能当做第三方库被别人使用
internal class Person{
var name = ""
} -
fileprivate
加入在main.swift里定义了一个Person
类,那么Person
类只能在main.swift
文件中使用 -
private
, 下面name
只能在Person{}这个大括号里使用
class Person{
private var name = ""
}
访问控制.png
- 绝大部分实体都是
internal
级别
访问级别的访问准则
1.png 变量类型>= 变量的访问级别.png 父类型>=typealias.png 原始值类型.png 定义A类型.png元组类型
元组类型.png泛型类型
泛型类型.png成员、嵌套类型
成员、嵌套类型.png 1.png-
上面代码在全局作用域中,2个作用域类型是一样的。private和fileprivate是一样的
-
private是在定义的文件里访问,在全局作用域,就是当前的文件里都能访问
2.png -
private和fileprivate是在Test里面,不符合
父类高于子类的规定
。
- 上面Dog是private,但是在全局作用域里,private相当于fileprivate,那么age也是fileprivate
直接在
全局作用域下
定义的private
等价于fileprivate
重点
5.png- 首先上面代码是运行成功的。
问Person里面的dog.run()为啥不报错
-
因为
Dog和Person都是private修饰,意味着Dog是在Test类里
可以使用,那么Dog的run()函数和age
都是可以在Test类里
使用的
- 上面代码
age
加了一个private,那么age只能在定义它的实体里使用,也就是在Dog里面使用,外部使用不到。
getter setter
getter setter.png初始化器
初始化器.png 结构体.png- 一旦上面的y是private,那么Point(y:10)会报错,因为带y的初始化器只能在Point里面使用
枚举
枚举.png协议
协议.png- 针对于最后一条理解:协议有定义协议的访问级别,有实现协议里面方法的访问级别,当
实现协议里面方法的访问级别
>=定义协议的访问级别
,就好比下面代码
定义协议的访问级别 private
private protocol A{
run()
}
class Person:A{
实现协议的访问级别 public
public run(){}
}
- 上面代码不报错
扩展
扩展.png 扩展1.png- Person扩展里
run()
函数,在别的文件里不能被调用,只能在当前文件里调用
,run()的访问级别是fileprivate
扩展2
扩展2.png- 上面3个扩展都是在同一个文件中,相当于所有代码都在同一个类中。
将方法赋值给var/let
struct Person {
var age:Int
func run(_ v:Int){ print ("func run",age,v)}
static func run(_ v:Int){ print ("static func run",v)}
}
let fn1 = Person.run;
fn1(10); //static func run 10
let fn2:(Int)->() = Person.run;
fn2(20); //static func run 20
- 上面代码,先传一个Person实例,在调用函数run
- 实例方法不用传Person实例
内存管理
内存管理.png 内存管理-2.png自动释放池
自动释放池.png循环引用
循环引用-1.png闭包循环引用
- p强引用闭包,闭包又强引用p
-
()
这个是马上调用,就把age值赋给了getAge,闭包表达式不存在了,闭包表达式self.age执行完生命周期就完了,相当于getAge本质是Int类型,getAge=self.age,所以这就不用写weak
@escaping
escaping.png- 如果是逃逸闭包必须写self,因为影响了self的生命周期。
逃逸闭包注意点2
逃逸闭包.pngdo
do.png内存访问冲突(Conflicting Access to Memory)
内存访问冲突.png 内存访问冲突-2.png指针
指针.png-
UnsafeMutablePointer
是可以修改指针的值的 -
pointee
是可以取出指针的值的,相当于C里面的*ptr
-
UnsafeMutableRawPointer
不知道传递的指针是什么类型,所以storeBytes()
里面的as
是类型
。 -
load
里面的as
也是类型,这个函数是取值。
指针使用场景
指针使用场景-1.png获得指向某个变量的指针
获得指向堆空间实例的指针
- ptr存储的是堆空间的指针,不是person 的指针
-
UnsafeRawPointer
就是把指针指向的地址取出来,这里ptr3
拿到的是person的地址,heapPtr指向的是person指针指向的堆空间的地址。
- ptr存储的是person 的指针,unsafePointer传谁的值,返回谁的值,这里是person的值,返回person的值。
创建指针
创建指针.png 创建指针-2.png- malloc 开辟了16个字节的数据,然后 store前8个字节存11,接着store(offSet)偏移8个字节,存22。
-
advanced
指针挪动8个字节,返回给你一个指针,是下一个8个字节的指针,2个指针之间相差8个字节。相当于 0x00 + 8个字节 = 0x08,返回给你。 - 这里rawPointer不知道那个类型,所以得传类型。
- 这里指定了类型Int,这是
capacity
是容量,相当于开辟了2*8=16个字节。
-
initalize
是 初始化前8个字节,也就是0x000 -
successor
指向的内存是 后8个字节,0x008
- 调用了
initalize
- 最后 得掉
deinitalize
释放
指针转换
指针转换-1.png-
ptr.assumingMemoryBound
是把任意类型转一种类型
- 把ptr转成Int,就相当于把二进制数据给你,然后类型发生变化,原来是任意类型,现在是Int。
字面量(Literal)
字面量.png字面量协议
字面量协议.png字面量协议应用
字面量协议应用.png模式匹配
通配符
通配符-规则.png 通配符--代码.png标识符模式
标识符模式.png值绑定模式
值绑定模式.png元组模式
元组模式.png枚举case模式
枚举case模式.png可选模式
可选模式.png-
?
非nil,非空值
类型转换模式
类型转换模式.png自定义模式
自定义模式.png自定义模式表达式
自定义模式表达式.pngwhere用处
where.pngOC到Swift
标记
标记.png编译条件
编译条件--1.png 编译条件--2.png系统版本检查
系统版本检查.pngAPI可用性
API可用性.pngSwift掉OC
Swift掉OC--1.png person.h.png person.m.pngSwift调用.png
Swift掉OC --- @silgen_name
Swift掉OC --- @silgen_name.pngOC掉Swift
OC掉Swift .png OC掉Swift -代码.png OC掉Swift-@objc.png选择器Selector
选择器Selector.png1. 为啥Swift暴露给OC的类最终要继承NSObject?
- 这个类是在OC里面用,在OC里面用肯定得继承NSObject,得有isa指针,最终走objc_msgSend。
- OC依赖与runtime,runtime要求类有isa指针,isa指针肯定继承NSObject。
2. Swift类调用OC类里方法,是怎么调用的?反过来OC调用Swift又是怎么调用的? - 纯Swift掉方法是走虚表那套机制
Call 0x0xxxx 找这个地址
- Swift掉OC的方法,还是走的objc_msgSend机制
- OC掉Swift的方法,肯定也是走objc_msgSend,因为继承NSObject,然后依赖isa
下面的类里run怎么走?
- 还是走Swift虚表这套。在Swift里面,肯定走Swift里面的效果。
String
字符串一.png 插入和删除.png-
endIndex
指的是2
后面的位置 -
startIndex
指的是1
的位置
Substring
- string为Hello,world 然后Substring那你截取的话,Substring同样指向
Hello,world
的地址,2个公用一份地址,然后把数据返回, - 如果要把Substring类型转成string,那么就得吧Substring地址的数据深拷贝出一份,形成一个新的地址。
String相关协议
String和NSString
String和NSString.pngSwift和OC桥接转换表
Swift和OC桥接转换表.png- 上面的是直接
as
只能被class继承的协议
协议--1.png 协议--2.pngdynamic
dynamic.pngKVC、KVO
KVC、KVO.pngblock版本的KVO
关联对象
关联对象.png-
Void
是一个字节,不浪费内存
资源名管理
资源名管理-1.png 资源名管理-2.png资源名管理的其他思路
资源名管理的其他思路.png多线程开发---异步
多线程开发---异步.png数组操作map
- map会遍历元素,遍历一次,调用一次闭包,然后将元素传给你,操作,然后返回一个值,然后将返回值,放到一个新的数组,返回给你。
数组操作filter---过滤
- filter会遍历元素,遍历一次,调用一次闭包,然后将元素传给你,操作,然后返回一个Bool值,如果返回为true,那么就返回到数组里面去
数组操作reduce
reduce.png- reduce函数,接收2个参数,第一个参数初始值,第二个参数闭包返回值为result,类型跟初始值一样。
- 循环遍历数组中每个元素,第一次遍历,result为初始值,上面代码为0,element为1,2个数值相加,作为下次循环的result--
1
- 第二次循环,也就是2了,element为2,result为第一次循环的结果
1
,然后2者相加,结果result3
,作为下次循环的结果, - 始终result作为下次循环的参数,和element相加,一直到循环结束,赋值给
arr2
- 这里就相当于0+1+2+3+4
map和flatmap区别
- (1)flatMap返回后的数组中不存在nil,同时它会把Optional解包
let array = ["Apple", "Orange", "Puple", ""]
let arr1 = array.map { a -> Int? in
let length = a.characters.count
guard length > 0 else { return nil }
return length
}
arr1 // [{some 5}, {some 6}, {some 5}, nil]
let arr2 = array.flatMap { a-> Int? in
let length = a.characters.count
guard length > 0 else { return nil}
return length
}
arr2 // [5, 6, 5]
- (2)flatMap还能把数组中存有数组的数组(二维数组、N维数组)一同打开变成一个新的数组
let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let arr1 = array.map{ $0 }
arr1 // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let arr2 = array.flatMap{ $0 }
arr2 // [1, 2, 3, 4, 5, 6, 7, 8, 9]
- (3)flatMap也能把两个不同的数组合并成一个数组,这个合并的数组元素个数是前面两个数组元素个数的乘积
let fruits = ["Apple", "Orange", "Puple"]
let counts = [2, 3, 5]
let array = counts.flatMap { count in
fruits.map ({ fruit in
return fruit + " \(count)"
})
}
array // ["Apple 2", "Orange 2", "Puple 2", "Apple 3", "Orange 3", "Puple 3", "Apple 5", "Orange 5", "Puple 5"]
- map:可以对数组中的每一个元素做一次处理
compactMap
- 当闭包中的返回结果是可选的时候,使用compactMap代替flatMap,那么当闭包中的返回结果不是可选的时候,依然使用flatMap。
let arrayString = ["Ann", "Bob", "Tom", "Lily", "HanMeiMei", "Jerry"]
let arrayInt = arrayString.compactMap { (str) -> Int? in
return str.count
}
print("arrayInt: \(arrayInt)")
// arrayInt: [3, 3, 3, 4, 9, 5]
// 简化
let arrayInt2 = arrayString.compactMap { $0.count }
print("arrayInt2: \(arrayInt2)")
// arrayInt2: [3, 3, 3, 4, 9, 5]
let arrayI = arrayString.compactMap { $0.contains("i") ? $0 : nil }
print("arrayI: \(arrayI)")
// arrayI: ["Lily", "HanMeiMei"]
- 结果可以看出,虽然闭包返回值是可选的,但是真正返回的结果中,并不是可选的,也会过滤掉nil的情况。
lazy的优化
lazy的优化.png lazy的优化-2.png- 看上面代码
lazy
在需要的时候才去进行操作,而且result[1]
的时候,只是去map2这一个元素,当这数据量大的时候,可以使用lazy进行优化。
option的Map和flatMap区别
option的Map和flatMap区别.pngoption的Map
- 如果有值,那么取值以后进行map操作,操作再加一层Option,那么返回的是
??
这种格式 - 如果没值,那么直接返回nil
option的flatMap - 如果有值,那么取值以后进行flatMap操作,操作再加一层Option,那么返回的还是
?
这种格式
高阶函数
高阶函数.png柯里化Currying
柯里化.png函子(Functor)
函子-1.png 函子-2.png适用函子(Applicative Functor)
适用函子--1.png 适用函子--2.png单子(Monad)
单子.png-
F
是类型
面向协议编程
面向协议编程-1.pngOOP
OOP不足
POP解决方案上面不足
POP注意点
计算属性
- 内存里没有一个存储属性来指向他
- 本质是方法
利用协议实现前缀
利用协议实现前缀-1.png Base代码.pngBase协议
-
Base后面继承一个协议
Base协议.png
利用协议判断类型
利用协议判断类型--1.png响应式编程
响应式编程.pngRXSwift
RXSwift.pngRXSwift核心角色
RXSwift核心角色--总览.png创建、订阅Observable1
创建、订阅Observable1.pngObservable.just(1111)
等同于
// 发送消息
observable.onNext(22222)
// 发送多个消息,然后接收方 接收3次
Observable.of([1,2,3])
// 发送多个消息,然后接收方 接收3次
Observable.from([1,2,3])
Observable.of和Observable.from打印结果.png
创建、订阅Observable2
创建、订阅Observable2.png-
seconds(3)
3秒之后再发消息 -
period: .seconds(1)
周期,3秒之后,每隔1秒再发消息 -
scheduler
:MainScheduler.instance 主线程 -
上面就是个定时器
Disposables
Disposables.png-
DisposeBag()
是上面bag对象 -
self.rx.deallocated
跟随着self的生命周期,当self销毁的时候,自动释放。 -
bind
把数据绑定到对应的控件上去
创建Observer
创建Observer.png扩展Binder属性
-
Binder是Observe
扩展Binder属性.png
RXSwift的状态监听1
RXSwift的状态监听1.pngRXSwift的状态监听2
RXSwift的状态监听2.png即是Obeservale又是Obeserver
1.png-
ControlProperty
即是Obeservale又是Obeserver
Swift源码
Swift源码简介-1.pngSwift源码地址.png
Array源码分析
map源码
map源码.png-
interator
迭代器,interator.next()
迭代器拿到一个元素,传到transform()
函数里面,并且把transform()
的返回值放到result
里。 - 最终把
Array(result)
返回
filter源码
filter源码.png- 调用
_filter
函数 -
isInclude()
函数,就是filter穿进去的判断函数, -
interator
迭代器,interator.next()
迭代器拿到一个元素, - 把这个元素传给
isInclude()
函数,返回true,将这个元素拼接到result
数组里面 - 最终把
Array(result)
返回
flatMap compactMap reduce
flatMap
- 定义一个
result
数组 - 循环遍历,拿到element,传给
transform函数
- 把结果添加到
result
里 - 返回
result
compactMap
-
compactMap函数
调_compactMap
-
_compactMap
函数,循环遍历元素,然后判断元素不为nil
,添加到result
里 -
返回
result
-
`compactMap函数 去掉数组中的nil值
Substring
Substring--1.png Substring--2.png-
base
函数,拿到之前指向的字符串,是因为返回_slice.base
,就是外面传进来的字符串 -
_slice.base
里bounds记录着字符串是那段区域
Substring的append方法
-
var string = String(self)
,append函数里
,substring
把自己穿进去,创建一个新的string, - 然后创建一个新的地址,把原来内容拷贝进去,
- 把外面传进来的内容,拼接到新的string里
- 然后再拿新的string创建一个新的Substring
Opition的map和flatMap源码
flatMap
- 枚举值,先判断自己是否有值,如果是
.none
,就是没值,返回.none
- 如果有值 取出来赋值给y,调用
transform函数
, - 返回
transform函数
的值
map
- 枚举值,先判断自己是否有值,如果是
.none
,就是没值,返回.none
- 如果有值 取出来赋值给y,调用
transform函数
,拿到返回值, - 再包装一层
.some(返回值)
返回回去
区别
- 就是在有值,返回的时候,flatMap是直接返回
transform函数
的值 - map是包装一层
transform函数
的值,.some(transform(y))
;
Opition的==号
==代码1.png-
==代码2.png==
左边有值,右边为nil
-
上述代码可以看出
rhs
可以为nil,因为继承的协议可以初始化为nil
var age :Int? = 10
print(age == nil)
会触发上面的==代码
- 判断
lhs
是不是为nil,如果是nil返回true,如果有值返回false
var age :Int? = 10
print(nil == age)
会触发下面的代码
-
左边为nil,右边有值
==代码3.png
==比较2个正常的值
var age1 :Int??? = 10
var age2 :Int? = 10
var age3 :Int = 10
print(age1 == age3)
print(age1 == age2)
print(age2 == age3)
- 先拿到左右2边的值,左右2遍的值能解包,就取值,返回
左边值 == 右边值
这个判断 - 如果2个值都是nil,返回true
- 默认返回为false
Opition的??号 源码
var age1 :Int? = 10
var age2:Int? = 10
print(age1 ?? age2)
??代码1.png
??代码2.png
-
??
一个返回值是T
,一个是T?
- 都先判断左边的值是否有值,拿到左边的值先解包,有值都返回
value
,如果返回是T?
,不代表返回的就是可选类型。 - 左边有值,肯定得解包一次
- 没值,返回,调用右边的
defaultValue
闭包
Metadata分析
class Person{}
var person = Person()
- person指向堆空间,前8个字节指向
Metadata
- 中间8个字节是引用计数
- 在后面就是属性变量、函数等等
Metadata
-
Metadata
分类型的,struct、protocol、class的Metadata
不一样。
网址.png
ClassMetadata
-
kind
前8个字节,类型,是枚举、类、struct -
superclass
再8个字节
反射
反射.png关于Sting的考虑
关于Sting的考虑.pngvar str1 = "0123456789"
- str1在内存里占16个字节,内存里存储的是ASCii码,
- 上面是小端模式读取内存的结果,小端模式是37在前,
-
0
对应的ASCII码值是30
,那么9
对应的值是39
- 上面
str1
里面存储的值就是"0123456789"
-
0xe9
,a
就是长度(10个字符,0~9不就是10个字符吗),a最大是f,,e
是类型,表示直接存储内容, -
a
最大是f
,刚好填满15个字节,
存储了15个字节.png - 上面的str1,直接将内容放到了内存中,类似于OC的
target point
,直接将内容放到了内存中
var str2 = '01234567890ABCDEF'
打印地址.png
16个字符
- 汇编调用函数,
返回值
如果大于8个
字节,放到rax和rdx
- 上面的代码会调用
Swift.init
初始化器,传了2个参数,一个是字符串的长度,一个是字符串的真实地址,会拿15跟字符串的长度进行比较,小于15
直接存储在字符串的真实地址里, -
大于15
,会把真实地址放到str2的后8个字节,因为str2是全局代码,这8个字节存储在TEXT段的Cstring段,常量区
字符串真实地址 + 0x7fffffffffffffe0 = 0x800000010000a790
也可以
字符串真实地址 + 0x20 = 0x800000010000a790
-
读取地址,拿到对应的值
真实地址.png
字符串拼接 append
- 如果拼接完字符长度
不够15
,那么还是存储在内存中 - 如果
超过15
,那么重新指向
一块内存,那么就需要堆空间
了
从编码到启动App
从编码到启动App.png字符串拼接
字符串拼接.png- 看打印的地址,2个前8个字节,
0xd000000010
,0xf00000011
,这里面11和10 ,分别是字符串的长度,d0
表示在常量区
, -
f0
之后会调用String.init*()
->malloc
,重新分配地址 -
str2
后8个字节放的是,append之前的地址值,这个地址值+0x20就是堆空间的地址值,
str2 append之前后8个字节地址值 + 0x20 = 拼接后字符串地址值
-
0x20
32个字节,放的是堆空间信息。
dyld_stud_bind 符号绑定
App加载流程.png mac-o加载.pngvar str1 = "0123456789"
str1.append('ABCDEF');
append调用可String.init()函数。
- 底层调用了
String.init()
函数动态库,程序运行中才会把动态库载入进去,String.init()
的地址,是程序启动之后才知道的, - 编译的时候。
str1
的地址是临时的,占位地址
- 然后会执行
0x1000a3ce jump *0x2c4c(%rip)
,0x2c4c就是一个地址
rip = 0x1000a3ce + 6 = 0x100000A3D4
0x100000A3D4 + 0x2c4c = 0x100000D020
- 取出 0x100000D020的地址,然后jump
取出的值
,直接拿D020,去mac-o里面查地址,
mac-o文件的地址.png
取出的地址:0x0100000A5F4
- jump跳到
0x0100000A5F4
后,一直进,会来到dyld_stud_bind
- 绑定完了只会,再调到
真正的String.init()
函数地址
。
Array 值类型
var arr = [1,2,3,4]
-
arr占用了8个字节,是一个指针,指向堆空间。
array地址打印.png -
抛过前4个指针(32个字节),从第5个指针看,是
1
- 上面的
容量
,初始化的时候,是一定的,当元素数量
超过容量的值
的时候,容量
会翻倍扩容(舍弃一部分空间,重新开辟一段新的内存)
可选项本质
enum.pngvar age:Int? = 10
switch age {
case let v:{
print(v)
}
- 上面代码相当于直接把
let v = age
,直接把age无条件赋值给了v。不管是不是nil。
溢出运算符(OverFlow Operator)
var v1 = UInt8.max;
v2 = v1.&+1;
print(v2) // 0
var v1 = UInt8.min;
v2 = v1.&-1;
print(v2) // 255
var v1 = UInt8.max;
v2 = v1.&*2; == 255 &+255
// 因为 255 &+1 = 0 255 &+2 = 1 ... 所以为254
print(v2) // 254
- 取值范围是
0~255
,换算成数组长度的话就是 count = 256,如果 255 + 1 = 256 - count = 0。
溢出运算符-1.png
运算符重载(Operator Overload)
运算符重载.png-
prefix
-前置
++a;
-
postfix
-前置
a++;
Equatable
Equatable.png-
Equatable
继承这个协议,实现static func == () ->Bool
函数
func equals <T:Equatable> (_ t1:T, _t2:T) ->Bool{ t1 == t2}
-
<T:Equatable>
类型为T
,都继承了Equatable
协议
Comparable
Comparable--代码1.png Comparable--代码2.png自定义运算符(Custom Operator)
自定义运算符-说明.png-
assugnment:true
,可选链中拥有赋值一样的优先级
class Person{
var age = 0
}
func getAge() -> Int{ 10 }
var p:Person = Person();
p?.age = getAge()
赋值优先级
,上面代码 p?
要是p有值,getAge()
计算出结果,才会赋值给age,如果p为nil,那么就不会赋值
associativity
:结合性,
- left 是从左到右计算
- right 是从右到左计算
- none 不允许出现2个以上的运算,下面代码把
left
改成none
会直接报错。不知道该怎么计算。
扩展(Extension)
扩展.png-
存储属性、继承
增加内存结构,所以扩展不能添加这些。
协议、初始化器
协议、初始化器.png上面最后一条解释 ---- 类实现required
协议
协议1.png 协议2.png- 如果类里函数名称和协议里函数名称 一样,调用的时候,优先调用类里的函数。
网友评论