数据结构*
计算机中存储、组织数据的方式
数据结构是一种具有一定逻辑关系,在计算机中应用某种存储接口
集合结构 线性结构 树形结构 图形结构
数据结构的存储
顺序存储 链式存储结构
单向链表 双向链表 循环链表
数组和链表区别:*
数组: 数组元素在内存上连续存放, 可以通过下标查找元素; 插入、删除需要移动大量元素, 比较适用元素很少变化的情元素在内存中况
链表:
链表中的元素在内存中不是顺序存储的 , 查找慢, 插入、删除只需要对元素指针重新复制, 效率高
二叉树
SDWebimagecach原理
内存缓存
磁盘缓存
网络下载
Code manager
图片解码
图片解压缩
内存设计需要考虑的问题
内存记录缓存
磁盘存储
定期写入磁盘
每当达到某个值的时候,
就写入磁盘
二: 进程 线程
进程 系统操作资源的基本单元
每个进程之间是独立的, 每个进程均运行在其专用且受保护的内存空间里
程序执行流的最小单元, 线程是进程中的一个实体
一个进程要想执行任务, 必须至少有一条线城
系统默认开启一条线程, 也就是主线程
线程是进程的执行单元, 进程的所有任务都在线程中执行
线程是cpu分配资源和调度的最小单位
线程是进程的执行单元 进程的所有任务都在线程中执行
能适当提高程序的执行效率
能适当提高资源利用率(cpu、内存利用率)
开启线程需要占用一定的内存空间
线程之间通信, 多线程的数据共享
任务
执行任务有两种方式: 同步执行和异步执行
常见的数据结构:
栈 特殊的线性表
队列 特殊的线性表
数组 聚合结构
链表: 链式结构
树: 非线性结构
图: 非线性结构
堆: 特殊的树形结构
散列表
常用算法:
检索
插入
删除
更新
排序
插入排序(少量元素排序)
平均时间复杂度 0(n^2空间复杂度:O1
浅拷贝: 指(内存地址的复制)针拷贝 让目标对象指南和原对象指向同一片内存空间
会增加引用计数
深拷贝: 对象内容的复制 开辟新的内存空间
可变对象的copy和mutablecopy都是深拷贝
不可变对象的copy是浅拷贝 , mutablecopy是深拷贝
Copy 方法返回的都是不可变对象
Copy方法的都是不可变对象
Array对象实际上是不可变的, 如果对其进行copy
会造成程序crash
数据结构和算法*
线性结构 集合结构 树形结构 图形结构
数组: 查找数据复杂度O1 查找数据复杂度低 , 插入删除效率低
字典和集合: 查找数据复杂度O1
链表: 插入 删除效率高 查找效率低
栈和队列
栈是先进后出 后进先出
队列: 先进先出
二叉树(二叉树的题目都可以用递归来解)
深度 二叉树找树
最常见的树的遍历: 前序 中序 后序
层级遍历 又叫 广度优先遍历
排序和搜索
冒泡排序 插入排序 选择排序 时间复杂度: O(n2) 空间复杂度:n
堆排序 归并排序 快速排序 时间复杂度: nlogn
空间复杂度分别为 1 n logn
桶排序: 时间复杂度 n 空间复杂度 k
一般情况下, 好的排序算法性能是 nlogn 坏的排序算法 n平方
搜索: 二分搜索
二分搜索算法复杂度 logn**
2. 语言工具 swift
Swift 是苹果公司主推的编程语言
从性能上讲: 它的速度是Objective-c的2.6倍
类和结构体的区别
在swift中 class是引用类型 struct 是值类型
类是引用类型 struct结构体是值类型
引用类型是在堆上进行存储和操作的 而值类型 如结构体 则是在栈上进行存储和操作的
堆上操作更复杂 苹果公司官方推荐使用结构体
Class的优点
可以继承 类型转换可以在运行时检查和解释一个实例的类型
可以用deinit来释放资源 一个类可以被多次引用
Struct优势
结构较小, 适用于复制操作 相比class实例, struct更加安全
无须担心内存泄漏或者多线程冲突
Swift是面向对象还是函数式编程语言
即使面向对象 优势函数是编程 因为swift支持map, reduce, filter, flatmap这类去除中间状态 更加强调运算结果而不是中间过程
在swift中, 什么事可选型optional
在swift中 可选型是为了表达当一个变量值为空的情况
泛型 主要是为增加代码的灵活性而生的; 它可以使对应的代码满足任意类型的变量或方法
Swift有五个级别的访问控制权限, 从高到低依次为open public internal,
File-private private
高级班的遍历不逊序被定义为低级别变量的成员变量
高级别变量不允许被定义为低级别变量的成员变量
Open具有最高权限, 其修饰的类和方法可以被任意Module中被访问和重写、
Swift的内存管理机制与Objective-c一样, 都为ARC, 基本原理是:
一个对象在没有任何强引用指向它时, 所占用的内存会被回收
加入weak和unowed 是为了解决有stong带来的循环引用问题
在swift中, 如何理解copy on write
当值类型复制的时候, 复制对象和原对象实际上在内存中指向同一个对象
当且仅当修改复制后的对象, 才会在内存中重新创建一个新的对象
因此内存的使用更高效
当两个对象互相有一个强引用指向对方时, 就会导致两个对象在内存中无法被释放
什么是属性观察
属性观察是指在当前类型内对特定属性进行监视, 并做出响应的行为
属性观察是swift的特性, 具体有两种; willset和didset
对当前类型内特定属性进行监视, 初始化方法对属性的设定, 不会出发调用属性观察
下面举一个例子
Var title: String{
}
# autoclosure 可以讲表达式右边的值的计算推迟****
Swift有函数式编程的思想, 其中flatmap ****map ****reduce ****filter是其代表的方法
ARC: 自动引用计数
简单来说就是代码中自动加入了re tain 和release , 原先需要手动添加用来处理内存管理的引用计数的代码可以自动地由编译器完成****
ARC是在编译时管理内存, 垃圾回收在运行时管理内存
循环引用是指两个或两个以上对象互相强引用, 导致所有对象无法被释放的现象, 这是内存泄漏的一种情况
Strong 强引用
Weak 弱引用
Assign 主要修饰基本数据类型
Weak 一般用来修饰对象
Assign 一般用来修饰基本数据类型
原因assign修饰的对象被释放后, 指针的地址依然存在, 造成夜指针
Strong复制的多个指针指向同一个地址
而copy的复制是每次会在内存中复制一份对象, 指针指向不同地址
Copy 一般用在修饰有对应可变类型的不可变对象上, Nsstring NSArray 和 Nsdictionary
Atomic比nonatomic相对线程安全, 修饰的对象会保证setter和getter的完整性
当多个线程同时调用set和get时, 就会导致获得的对象值不一致
R unloop 是每一个线程一直运行的一个对象,
主要负责响应需要处理的各种事件和消息, 每一个线程都有且仅有一个runloop与其对应
没有线程, 就没有runloop
__weak 修饰变量 weak修饰属性
__block 也用于修饰变量, 它是引用修饰, 所以, 其修饰的值时动态变化
即可以被重新赋值的, __block用于修饰某些block内部将要修改的外部变量
Block就是没有名字的函数 或者 可以理解为指向函数的指针
Block容易造成循环引用 , 解决的办法是用__weak关键词修饰变量
Block更适合 轻便 简单的回调, 如网络传输
而代理适用于公共接口较多的情况, 这样做也更易于解藕代码架构
用strong修饰会有Nsstring被修改的功能 copy表示改属性不能修改
滑动页面上的列表时, 当前线程的runloop切换了mode的模式
Default模式切换到了eventtrack模式
[nsrunloop currentrunloop] addtimer:timer forMode: NSRunloopCommonModes
Objective-c是一门以C语言为基础的语言, 所以天生具备C语言的缺点, 各种弊病积重难返
Swift注重安全性 objective-c注重灵活性 swift有函数式编程 面向对象编程 和面向协议编程
而objective-c几乎只有面向对象编程
Swift更注重值类型的数据结构 而objective遵循C语言老一套, 注重指针和索引
Swift是静态类型语言 objective-c是动态类型语言
Copy-on-write 又将值传递和复制的开销降到最低
值类型相比引用类型, 最大优势在于可以高效地使用内存, 值类型在栈上操作, 引用类型在堆上操作 栈上操作仅仅是单个指针的上下移动, 堆上的操作则牵涉合并、以为、重新链接
值类型也是为了线程安全, 通过swift let设置 使得这些数据达到真正意义上的不变
Swift obj 两者都可以被用作代理
实际开发主要用于适配器模式
Swift中的protocol还可以对接口进行抽象
Runtime其实就是objective-c的动态机制, runtime执行的是编译后的代码
Objc_msgsend(self.tableview, @selector(reload))
编译阶段: 编译起会翻译成 objemasdsend
运行阶段 直接执行 转发消息 也有可能找不到方法导致程序崩溃
Swift目前被公认为静态语言, 它的动态特形通过侨界OC来实现
Message send 找不到对象分两种情况 , 对象为空 对象不为空
Objc
Method swizzling
每个类维护一个方法列表, 方法名预期实现是一一对应关系
即SEL方法名和IMP实现指针的对一个关系
Method 可以在runtime将Sel和Imp进行更换
在objc中 正常情况 在类别中添加属性会报错
提示找不到getter和setter方法
Autolayout性能比frame要差
UIView用于交互对象 , 主要用于用户触发的各种操作, 事件处理响应
CALayer 在图像和动画渲染上性能更好
Frame当前视图相对父视图的平面坐标系统中的位置 大小
Bounds 是指当前视图相对于自己
Get的参数在其URI里, POST的参数在其包体里 - 从这个角度来看, POST比GET安全
Session是服务器用来认证、追踪用户的数据结构
客户端打包请求 服务器端接受请求 服务器端返回数字证书 客户端生成加密信息, 客户端发送加密信息
本地消息通知流程:
注册 创建 推送 响应
设计模式 MVC 装饰模式 适配器模式 外观模式 单例模式 观察者模式 备忘录模式
Model 负责处理数据 View处理UI; controller是View和model的桥梁, 它将数据从Model层传送到View并展示出来
同时将View层的交互传到Model层以改变数据
swift3.0中访问控制一共有5个关键字
1. open:可以在任何地方访问、继承和重写
- public:可以在任何地方被访问,在其他module中不能被继承和重写(一会在详细介绍一下什么是module内和module外)
3. internal:默认访问级别,在整个模块内都可以被访问
-
fileprivate:其修饰的属性可以再同一个文件被访问、继承和重写,同一个文件指同一个swift文件,一个文件中可以有多个类
-
private:其修饰的属性和方法只能在本类被访问和使用,不包括扩展类
someFunction(1, secondParameterName: 2)
“_”是代表函数调用时,可以忽略参数名称。
// 知识点:向下转型
// as! 强制类型转换,无法转换时会抛出运行时异常
// as?可选类型转换,无法转换时返回nil
假如有一个Person类,有一个run的实例方法。实例化一个对象p,[p run]是怎么执行的?
根据对象p的isa指针,找到所属的类
根据selector在类的cache缓存中寻找方法实现的地址,找到执行,没找到执行下一步
在类的methodLists方法列表中寻找,找到执行,没找到执行下一步
根据类的super_class指针,到父类的缓存中寻找,没找到执行下一步
在父类的方法列表中寻找,如果没找,继续向上找,一直到NSObject
如果最终没找到,则会调用resolveInstanceMethod或者resolveClassMethod方法,让我们可以动态添加方法实现
在OC中,方法的调用我们称之为消息发送,实际上方法的调用会首先调用C语言的
objc_msgSend(receiver, SEL)函数。
其中参数receiver为消息接收者,也就是调用方法的对象,类型为id类型。
参数SEL就是调用方法的字符串。
objc_msgSend函数主要做了做了如下几个事情:
通过receiver获取isa指针,如果是对象调用方法,isa指针指向的是对象所属的类。如果是类调用方法,isa指针指向的是类的元类
通过isa指针获取Class
先从类的缓存中获取方法的imp指针,如果缓存中没有,则从类的方法列表中获取,如果本类没有找到,则继续向父类的方法列表中获取,获取到imp指针后,将其存至缓存桶中(imp指针指向的是方法的实现地址)
如果没有找到imp指针,则进入消息转发流程,如果消息转发的回调方法没有被实现,则会引发崩溃
方法交换的本质是什么
由上面内容,我们可以知道,消息发送最终是寻找到方法的imp指针。在OC中每个方法都有一个imp指针,所以方法交换最终交换的是imp指针,系统给我们提供了一个method_exchangeImplementations函数,可以进行imp指针的交换。
KVO是怎样通过runtime实现的
利用runtime生成一个类,并继承自原对象类。让原对象的isa指针指向它,并重写它的set方法,并分别调用willChangeValueForkey方法和
didChangeValueForkey方法,当属性变化时,会调用这两个方法通知到外面属性变化
Swift支持runtime吗
Swift本身是一门静态语言,纯Swift类以及Swift结构体是不支持runtime的。
但Swift中仍然保留了很多继承自NSObject的类,这些类仍然支持runtime,但是受到了比较强的限制。比如在方法替换时,替换的方法需要加上@objc前缀,表示要使用oc的动态性。
GCD
同步与异步的区别
同步: 只能在当前线程中执行任务, 不能开启新线程
异步: 可在新的线程中执行任务 能开启新线程
队列:
并发队列: 可多个任务并发执行(会开启多个线程同时执行任务)
只在异步函数(dispatch_async)下有效
串行队列: 一个任务执行完毕后, 再执行下一个任务
主队列: 主队列中的任务都在主线程中执行,特殊的串行队列
objc在向一个对象发送消息时, runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行, 如果在最顶层的父类中依然找不到相应的方法时, 程序在运行时会挂掉并抛出异常unrecognized selector
元类和类的数据结构是同一个,只是运行时使用的字段不一样。
实例方法调用是通过objc_msgSend来调用,它的第一个入参就是实例对象,其流程是查找实例对象的isa指针,找到类对象,然后找到method_t的IMP,bl直接跳转调用。
类方法的调用和实例方法调用一致,它的第一个入参对象是类对象,类对象的isa指向的是元类。
所以,没有元类的话,类方法是没有办法调用的。objc_msgSend的调用流程是一定要isa指针的。
如果实例方法和类方法都放在类对象上,那类对象的isa指针只能指向自己了,那一旦类方法和实例方法重名,就没法搞了!
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !OBJC2
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use Class instead of struct objc_class * */
#endif
Runloop 是每一个线程一直运行的一个对象,
主要负责响应需要处理的各种事件和消息, 每一个线程都有且仅有一个runloop与其对应
没有线程, 就没有runloop
类对象中都有一个方法列表, 方法列表中记录着方法的名称, 方法的实现
以及参数类型,其实selector本质就是方法名称, 通过这个方法名称就可以在方法列表中找到对应的方法实现
类对象的数据结构:
类对象就是objc_class
isa 指向元类
superclass指向父类
cach 方法混存列表
data:顾命思义, 就是数据, 是一个被封装好的class_rw_t
iOS应用生命周期: 开始启动 启动完毕 程序进入前台 程序进入后台 程序进入非活动状态和程序将要终止等
IOS应用几种状态
非运行状态
不活跃状态
活跃状态
后台状态
挂起状态
objc对象的isa指针指向他的类对象,从而可以找到对象上的方法
编程中六大设计原则:
单一职责原则:
一个类只做一件事 CALayer: 动画和视图的显示 UIview只负责事件传递、事件响应
开闭原则
对修改关闭 对扩展开放
接口隔离原则
使用多个专门的协议、而不是一个庞大臃肿的协议 UITableviewDelegagte UITableviewDataSource
依赖倒置原则
抽象不应该依赖于具体实现、具体实现可以依赖于抽象
里氏替换原则
父类可以被子类无缝替换, 且原有的功能不受任何影响
迪米特原则
一个对象应当对其他对象尽可能少的了解, 实现高聚合、低耦合
二 如何设计一个图片缓存框架:
使用manager单例
内存缓存
磁盘缓存
网络下载
code Manager
图片解码
图片解压缩
图片的存储以图片的单向hash值为Key
优化TableView:
正确使用复用机制重用cells
尽量使所有的View 不透明
缓存行高
异步加载数据, 缓存请求结果
使用shadowPath来画阴影
减少subviews的数量
尽量不使用cellforrowatindexpath
使用正确的数据结构来存储数据
使用rowHeight, sectionFooterHeight和SectionHeaderHeigh固定高度,不要请求delegate
如何提升tableview的流畅度?
本质上降低CPU、 GPU的工作;
CPU: 对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像绘制
GPU: 纹理的渲染
卡顿优化在CPU层面:
1: 尽量使用轻量级对象,比如用不到事件处理, 可以考虑使用CALayer取代UIView
2: 不要频繁地调用UIView相关属性, 比如frame、bounds、tranform
3: 提前计算好布局,有需要时一次性调整对应的属性
4: 直接设置frame, 避免使用Autolayout自动布局
5: 图片size最好刚好跟UIImageView的Size保持一致
6: 控制一下线程最大并发数量 尽量把耗时操作放到子线程 文本处理(尺寸计算、绘制) 图片处理(解码、绘制)
卡顿优化在GPU层面:
1: 尽量避免短时间内大量图片的显示
2: Gpu能处理最大纹理尺寸是4096X4096,纹理不要超过这个尺寸
3: 尽量减少视图数量和层次
4: 减少透明的视图, 不透明设置opaque为YES
5: 尽量避免出现离屏渲染
程序的耗电主要在以下四个方面:
1: CPU处理
2: 定位
3: 网络
4: 图像
优化的途径:
尽可能降低CPU、GPU的功耗
定位采用使用时定位模式
尽量少用定时器
优化I/O操作 (不要频繁写入小数据 数据量比较大时用数据库)
网络方面的优化
减少压缩网络数据, 建议使用protoBuf
如果请求的返回数据相同, 可以使用NScache进行缓存
使用断点续传, 避免因网络失败后重新下载
网络不可用的时候, 不尝试进行网络请求
长时间的网络请求, 要提供可以取消的操作
什么是method swizzling:
简单来说就是进行方法交换
在objc中调用一个方法, 其实是向一个对象发送消息,
查找消息唯一依据是selector的名字
利用objc的动态特性, 可以实现在运行时偷换selector对应的方法实现, 达到
给方法挂钩的目的
每个类都有一个方法列表, 存放着方法的名字和方法实现的映射关系,
select的本质其实就是方法名, IMP有点类似函数指针, 指向具体的method实现, 通过select就可以找到对应IMP
利用Method_exchageImplementations交换两个方法的实现
利用class_replaceMethod替换方法的实现
利用Method_setImplementation来直接设置某个方法的IMP
什么时候会报 unrecognized selector的异常
objc在向一个对象发送消息时, runtime库会根据对象的isa指针找到该对象实际所属的类, 然后在该类中的方法列表以及其父类方法列表中寻找方法运行, 如果在最顶层的父类中依然找不到相应的方法时, 会进入消息转发阶段, 如果转发流程仍未实现,则程序在运行时会挂掉并抛出异常unrecognized selector sent