swift学习笔记
===数组(array)什么时候会发生拷贝?swift后台是很聪明的,其实它只有觉得确实有必要的时候才会去拷贝;1,在数组长度发生改变的时候 2,当调用unshare方法后,不过调用unshare方法也分2种情况,如果只被一个变量引用,也是不会拷贝的,只有被多个变量引用的时候才会发生拷贝 3,显示调用copy方法,这个时候就一定会发生拷贝
===字典的话,是否发生拷贝就决定于它的键值是什么类型的,如果键值都是值类型的,那就会发生拷贝,如果键值都是引用类型的,那就不会发生拷贝,补充一下的就是,结构体,还有基本的那些数据类型都是值类型的,所以键值是这些的时候,字典都会拷贝,只有类才是引用类型的
===实例声明或者赋值给常量后,它的属性还能不能被修改,这个要取决于它是什么类型的,如果是值类型的,像结构体这样的就不可以了,不过,如果是像类这样的引用类型的话,那它的属性就还是可以修改的
===nil在obeject-c中表示的是一个地址不存在的指针,而在swift中表示的是一个确定的值,这个值就是nil
===对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改,只要构造过程结束就修改不了了;不能在子类中修改,这个是对常量来说比较特别的地方
===swift中实例调用构造器时,内存初始化过程是怎么样的?首先当类的实例调用了其他构造器或者指定构造器去初始化一个类的对象时,先会去初始化当前类的所有存储的属性,确保所有存储的属性都有值了,完成了当前类的内存的初始化,然后再通过构造器代理,调用直接父类的指定构造器,然后再把直接父类的存储属性的内存初始化,这个时候就完成了直接父类的内存初始化,如果还有父类那就继续往上去初始化类的内存,直到最顶点的类完成内存初始化,这个时候就完成整个类的实例初始化过程的内存初始化,然后你才可以做其他的事情,比如调用其他实例方法,读取属性值,调用self
===如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允许。同样,你也不能使用隐式的self属性,或者调用其它的实例方法
===通过弱引用或者无主引用来取代强引用,我们可以解决强引用环问题(循环引用);具体就是对于生命周期中引用会变为nil的实例,使用弱引用weak,其实也就是可选类型的变量,只有是可选类型的变量的时候才能用弱引用来解决循环引用问题;对于初始化时赋值之后引用再也不会赋值为nil的实例,也就是非可选类型,使用无主引用unowned来解决循环引用问题
===循环引用存在第三种场景:两个属性都必须有值,且初始化完成后不能为nil。这种场景下,则要一个类用无主引用属性,另一个类用隐式展开的可选属性!,这样,在初始化完成后我们可以立即访问这两个变量(而不需要可选展开),同时又避免了引用环
===怎么定义隐式展开的可选属性?通过在类型结尾处加感叹号(City!),这样我们就声明了Country的capitalCity属性为隐式展开的可选类型属性;exp:let capitalCity: City!;就是说,capitalCity属性的默认值是nil,不需要展开它的值就可以直接访问
===解决闭包产生的强引用环:在定义闭包时同时定义占有列表作为闭包的一部分,可以解决闭包和类实例之间的强引用环;占有列表中的每个元素都是由weak或者unowned关键字和实例的引用(如self或someInstance)组成,类似[unowned self]这样的,用弱引用还是用无主引用还是看占有引用会不会为nil,如果会是nil,那就用weak,要不然就是unowned
===扩展(extension)可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器
===swift中常量的值不要求在编译时期确定,你可以在运行时再确定,不过你需要在使用之前必须赋值一次
===swift的if语句后面只能放bool类型的值,不能像其他语言一样放个整数就是真,0就是假这样
===如果枚举的变量是关联值,那你定义的枚举变量的内容最后都是存储在你定义的枚举变量里面,它占用的内存就是一个枚举变量一共占用的内存;如果是原始值(像case spring = 0, summer, autum, winter这样的),它是跟每个成员绑在一起的,固定的,那它的内存就是只存一个原始的数,只需要用一个字节,所以一个枚举变量到底占用了多少内存,你首先要分析的就是它是关联值还是原始值;
===如果枚举有成员值和关联值的话,它的内存排布是怎么样的呢?1,首先会用一个字节来存储成员值(前提是成员值有多个,也就是多个case,如果就一个case就不需要) 2,n个字节存储关联值(n取占用字节最大的关联值),任何一个case的关联值都共用这n个字节
===可选项是对其他类型的一层包装,可以将它理解为一个盒子;如果为nil,那么它就是个空盒子,如果不为nil,那么盒子里装的是被包装类型的数据
===如果要从可选项中取出被包装的数据(有值),需要使用感叹号!进行强制解包
===如果对值为nil的可选项(空盒子)进行强制拆包,将会产生运行时错误
===在某些情况下,如果一个可选项被设定后就一直有值,这个时候我们不必每次都是拆包,我们可以在变量后面加一个!,这样就是隐式解包,用的时候不用再加!号去拆包了
===malloc_size()这个方法是返回指针指向的堆空间的大小,你传入一个指针
===方法占用对象的内存吗?不占用,其实在一个类中,它的内存分布是这样的:前面8个字节是放类型信息的+8个字节放引用计数的+属性(存储属性占用的内存)
===那方法放在哪里呢?方法的本质就是函数,函数放哪里,方法就在哪里,函数放在代码段,所以说方法也是在代码段,
===iOS中内存分布从上到下,从小到大一般是:代码段(放一些函数,全局变量之类),数据区,堆区,栈区,大概是这样排布的
===结构体和类可以定义存储属性,枚举不可以定义存储属性,存储属性存储在对象实例的内存中
===计算属性的本质是方法,函数,不占用实例的内存,枚举,结构体,类都可以定义计算属性
===枚举的原始值是个只读(get方法)的计算属性,不占用对象的内存
===只有var才能访问延迟存储属性,let不可以哦,let必须在实例初始化完了要有值
===rdi,rsi,rdx这种寄存器是拿来给函数传值的(函数参数),rax一般用作函数返回值
===register read 寄存器名字,读取寄存器的内容
===值类型(枚举,结构体)不支持继承,只有类支持继承
===从父类继承来的存储属性一直都会有内存空间来存储它,不管你是不是把它重写为计算属性还是什么的,它会一直都在,那你在子类里面怎么访问父类继承来的存储属性呢?那就是使用super关键字啊,如果不加super关键字,那就是访问自己的属性的get方法,就会造成死循环
===子类可以将父类的属性(存储,计算属性)重写为计算属性,注意的是,只能重写为计算属性,不能重写为存储属性,不管你之前是存储属性还是计算属性,你都是只能重写为计算属性
===内存分区:代码区,全局区,堆空间,栈空间(从上到下,内存地址从小到大)
===swift的多态的底层实现类似与c++的虚表实现,都是提前把函数指针放在类型信息下面,而且这个东西编译的时候就可以确定,它调用方法就是先拿实例对象的指针指向堆空间的内存,然后取堆空间的内存的前八个字节指向的类型信息地址,然后通过类型信息的首地址+偏移量就可以找到要调用的方法的地址,这个要调用的方法的地址其实在编译期就已经决定好了哦,已经保存好了,调用的时候直接找到直接调用就可以了。
===所谓继承就是子类把从父类继承的东西拿到自己手中,父类的存储属性,子类也需要有内存空间给它,父类继承来的方法,子类也要在上面提到的类型信息的下面方法列表里把父类继承来的方法添加到自己的方法列表,这样你要调方法的时候才能找的到,总之就是父类的东西子类都要接收,保存好,放好
===内存地址从低到高:代码区,常量区,全局区(数据段),堆空间,栈空间,动态库
===x + 内存地址就是查看地址的内容;x/2xg + 内存地址,按规则打印地址内容,意思就是说如果你知道一个变量的地址,你想知道它的地址里面存的是具体的数据还是指针,你就可以用x+内存地址这个指令,如果后面再加上 /2xg这种的就是你怎么打印它的内容
===寄存器rax,rdx都可以拿来存放callq的函数返回值,每个存放8个字节
===iOS平台可执行文件格式是mach-o文件格式
===关于字符串变量底层存储问题:1,如果定义一个字符串的长度小于oxf的话,字符串的内容会直接存放在字符串变量的内存中(ascii的形式) 2,如果字符串的长度大于oxf的话,字符串的内容会存放在_text.cstring的常量区中,字符串的地址信息放在变量的后8个字节中 3,如果调用字符串的append方法,append后字符串长度还是小于oxf的话,字符串的内容依然存放在字符串变量的内存中,但是如果append后字符串的长度大于oxf的话,那就会开辟堆空间来存放字符变量的数据,有一点需要注意的是,常量区的数据是编译的时候就确定了,后面是不能更改的,所以如果之前数据在常量区,后面append后,其实也是会开辟新的堆空间来存数据的,因为常量区的数据不能更改,在编译期的时候就确定了哦,这点需要注意,堆空间的前面32个字节是拿来存放类型信息的,数据要在地址那里+ox20也就是32个字节的偏移量才到数据的真实内容
===可选项的本质是enum(枚举)类型的,有两个case,一个是.none,一个是.some,其中.some就是枚举的关联值,而且关联的类型就是泛型
===我们知道一个swift对象的内存结构是前8个字节存放metadata,然后有8个字节存放引用计数,接下来就是存放存储属性了,但是如果一个swift对象继承自oc的nsobject,那它的内存结构有变化吗?其实是有的,首先它会用前8个字节来存放isa指针,通过isa指针可以找到类的信息,然后接下来的字节就是存放对象的存储属性了,这个要记得哦,内存结构是会有变化的哦。
===swift对象的虚表在哪里呢?首先你创建了一个对象,然后对象的指针会指向堆空间的一块内存地址,然后堆空间内存地址 的前8个字节的内存存放的指针指向的就是metadata那块内存,然后接下来8个字节是引用计数,再下去就是存储属性的地址,那metadata那块内存又有什么东西呢?首先我们记得虚表查找的时候就是在metadata这里+一个偏移量,然后就找到了虚表的开头地址,这个就是虚表所在的地方,那虚表之前的那块内存存放的是什么数据呢?首先,不同的类型有不同的metadata,比如class和struct的metadata就是不同的,那具体又包含什么数据呢?这个的数据根abi有关的,swift5.0后abi就比较稳定了,也就意味着metadata的结构就稳定了,metadata首先的8个字节是isa指针,接下来是父类指针,再接下来是有16个字节保留给oc的runtime用的.....还有很多,注意我刚才说的这个是class类型的metadata,其他类型的metadata不一定是这样的哦,而且这个我也是就列举几个而已,其实想了解可以去github的doc里面看。
===swift常用的三方库:
1,网络请求:alamofire
2,图片下载:kingfisher
3,json访问:swiftyjson
4,json-model转换:kakajson