iOS中的数据持久化
NSUserDefault
Property list (属性列表)文件存储
Archive (归档)
SQLite本地数据库
CoreData: 最本质的特点是提供了一种将对象模型和关系模型进行映射的功能。
沙盒
作用:保存应用资源和数据
Documents目录:存储大文件或需要频繁更新的数据,可进行iTuns或iClouds的备份
Library目录:目录下有Preferences(能备份,存储应用的设置数据)和Caches目录(不能备份,存放数据缓存文件)。
Tmp目录:应用程序的临时目录,不能备份,随时会被系统清除。
为什么使用多线程
减少程序的响应时间
与进程相比,线程的创建和切换开销更小
多核CUP或计算机上,提高CPU的利用率
简化程序的结构,使程序便于理解和维护。(一个非常复杂的进程可以分成多个线程来执行)
多线程的缺点
代码会更加复杂难读,增加交接和维护的难读
创建和调度线程会有额外的开销,线程越多,开销越大
模型数据在多个线程间共享,要预防线程死锁情况的发生
在使用多线程时,需要适当开启线程,当线程使用完后,需要及时释放资源。
GCD实现线程同步方法
组队列(dispatch_group)
阻塞任务(dispatch_barrier_(a)sync)
信号量机制(dispatch_semaphore,可控制最大并发数)
GCD多线程编程中什么时候回创建新线程
只有异步提交任务时才会开启新线程,异步提交到串行队列会开启一个新线程,异步提交到并发队列可能会开启多个线程
同步提交任务无论提交到并发队列还是串行队列,都不会开启新线程,都会直接在当前线程依次同步执行
注意:如果当前线程是主线程,那么不可在当前线程提交同步任务,否则会造成线程死锁
iOS中如何触发定时任务或延时任务
performSelector实现延时任务
利用sleep实现后面的等待
GCD实现延时或定时任务
NSTimer实现定时任务
CADisplayLink实现定时任务
架构与设计模式
架构
MVC
MVVM (ViewModel与View进行数据绑定)
MVC的优点
高重用
低耦合
易维护性
有利于工程化管理
MVC的缺点
增加了系统结构和实现的复杂性
视图与控制器间的关系过于紧密
视图对模型数据的低效率访问
控制器的臃肿
缺少专门存放网络逻辑代码的部分
设计模式
工厂
单例(UIApplication、NSUserDefault、NSFileManager)
代理
观察者
策略模式
原型/外观模式
-
装饰模式
- 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
桥接模式
备忘录模式
生成器模式
命令模式
组合模式
如何检测内存泄露
静态分析
使用instruments检查
代码测试内存泄露(例如某些工具:facebook的工具xxx)
lldb常用的调试命令有哪些
help:打印所有命令和命令介绍
po或p:打印变量或对象,会调用对象的description方法
expression(e):调试时动态执行指定表达式,并将结果打印出来
lldb改变UI控件的属性
bt:打印调用堆栈,加all可打印所有thread的堆栈
操作系统
进程与线程
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,它是系统进行资源分配和调度的一个独立单位。资源包括:内存空间、磁盘空间、I/O设备等。
线程是进程的一个实体,是CPU调度和分配的基本单位。线程自己基本上不拥系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、寄存器、堆栈),但是它可以与同属于一个进程的其它的线程共享进程所拥有的全部资源。
进程是资源分配的基本单位。
线程是调度的基本单位。
一个程序至少有一个进程,一个进程至少有一个线程
线程的优点
易于调度
提高并发性
开销小。(相比进程)
有利于发挥多核处理器的性能
线程同步有哪些机制
临界区
互斥量
信号量
事件
内存管理有哪几种方式
块式管理
页式管理
段式管理
段页式管理
Cache替换算法有哪些
随机算法。RAND
先进先出算法。FIFO
近期最少使用算法。LRU,Least Recently Used
最优替换算法。OPT,OPTimal replacement
近期最少使用算法。LFU,Least Frequently Used
语言工具
Swift
静态类型语言
注重安全性(类型安全、线程安全)
函数式编程、面向对象编程、面向协议编程
更注重值类型数据结构(栈上,高效利用内存;写时复制)
Objective-C
动态类型语言
注重灵活性
面向对象编程
Swift协议
协议中的方法是动态派发,协议扩展中的方法是静态派发的。
协议中如果有方法的声明,那么方法会根据对象的实际类型进行调用。
若协议中没有该方法,那么此时只会按照扩展中的声明类型进行静态派发。
分析与优化
App启动时间过长,如何优化
理论上来说,App启动时间是由main()函数之前的加载时间(t1)和main()函数之后的加载时间(t2)组成的。
-
关于t1:需要分析App的启动日志,具体方法是在Xcode中添加 DYLD_PRINT_STATISTICS 环境变量,并将其值设置为1,得到启动日志。
动态库加载
重定位/绑定
对象的初始化
关于t2:主要是构建第一个界面并完成渲染的时间。所以,t2需要在具体的界面布局和渲染代码中进行打点观察,诸如viewDidLoad()和viewWillAppear()这两个函数就很值得关注。
如何使用Xcode检测代码中的循环引用
使用Xcode中的Memory Debug Graph。(在调试工具栏)
Instruments里面的Leak选项
怎样解决EXC_BAD_ACCESS
设置全局断点,快速定位缺陷所在。(效果一般)
重写Object的respondsToSelector方法。(效果一般,并且要在每一个class里进行定点排查,不推荐)
使用Zombie和Address Sanitizer:可以定位绝大多数
系统框架
storyboard/xib和纯代码构建的UI相比,有哪些优点和缺点。
关键词:可视化、多人协作、性能
优点:简单直接、调整关系清楚(storyboard)。
缺点:协作冲突、很难做到界面继承和重用、不利于进行模块化管理、影响性能(图层渲染)。
UIView和CALayer有什么区别
关键词:性能、交互
UIView和CALayer都是UI操作的对象。两者都是NSObject的子类,发生在UIView上的操作本质上也发生在对应的CALayer上。
UIView是CALayer用户交互的对象。UIView是UIResponder的子类,其中提供了很多CALayer所没有的交互上的接口,主要负责处理用户触发的各种操作。
CALayer在图像和动画渲染上性能更好。因为UIView上有多余的交互接口,而且相比CALayer还有层级之分。CALayer在无需处理交互时进行渲染,可以节省大量时间。
在iOS中实现动画的方式有哪几种
UIView Animation(移动、旋转、缩放、变色等基本操作)
CALayer Animation(同上,以及可回撤、暂停、与手势交互)
UIViewPropertyAnimator(iOS10中引进的处理交互式的动画接口,比CALayer Animation简单,为交互而生的动画接口)
如果一个列表视图滑动很慢,那么该怎么优化
- UI或数据出了问题
可能原因是
列表渲染时间较长。可能是因为某些UI控件比较复杂,或者图层过多。
界面渲染后。可能是因为大量的操作或耗时的计算阻塞了主线程
数据源问题。可能是网络请求太慢,不能及时得到相应的数据,也可能是需要更新的数据过多,主线程一时处理不过来。
针对以上上个问题解决
第一个问题,检查cell是否进行了复用。对于复杂视图的创建,可以采用惰性加载来推迟创建时间。尽量减少视图也是很好的优化方法。Facebook推出的ComponentKit就是很好的解决方案
第二个问题,使用多线程将复杂的计算放到后端线程处理,并进行缓存。如对于布局计算或非UI对象的创建和调整可以如此操作。。LinkedIn推出的LayoutKit就是很好的例子。
第三个问题,建议将网络端数据缓存并存储在手机端,将取得的部分数据根据优先级进行顺序渲染,还可以优化服务器端的实现来优化网络请求。
说说你平常开发中用到的设计模式
分:创建型、结构型、行为型。
MVC:它是应用的一种基本架构,主要目的是将不同的代码归并为不同的模块,做到低耦合、代码分配合理、易于扩展和维护。
单例模式(Singleton):此模式保证对于一个特有的类,只有一个公共的实例存在。它一般与懒加载一起出现,只有需要时才会创建。单例模式的例子有UserDefault.standard、UIApplication.shared、UIScreen.main等。
观察者模式(Observer):它定义对象之间的一种一对多的依赖关系,每当一个对象状态发生改变时,其相关依赖对象都会得到通知并自动更新。在iOS中的典型实现是NotificationCenter和KVO。
装饰模式(Decorator):它可以在不改变原来代码的基础上进行拓展。(例如Extention/类别、Delegation代理)注意,它与继承最大的区别是,继承时,子类可以修改父类的行为,而装饰模式不希望如此。
适配器模式(Adapter):它将一个类的接口转换为另一个类的接口,使得原来互不兼容的类可以通过接口一起工作。
外观模式(Facade):它用一个公共接口来连接多个类或其他数据类型。公共接口让多个类互相保持独立,解耦性良好。同时,使用接口时,外部无需理解其背后复杂的逻辑。另外,就算其接口背后的逻辑改变也不影响接口的使用。
备忘录模式(Memento):它在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到保存之前的状态。
苹果官方的MVC架构的优点和缺点
优点:代码总量少、简单易懂
缺点:controller中:代码过于集中、难以进行测试、难以扩展。Model层过于简单、网络请求逻辑无处安放
一言蔽之:代码分配过于笼统,主要放在Controller中。
MVCS中的S为什么要单独拆分出来
S是Store的缩写,存储。把Model或ViewController中拆分,成为存储层。
网络层可以放在存储层中。
代码分配更加均衡,便于测试。
MVP与MVC有什么异同
Model层一样
MVC中的View和Controller耦合在ViewController类中;而MVP中的View是单独的UIView/UIViewController,Presenter也是单独的类。
MVP中的View层是单独的Class,它持有Presenter作为变量。当接收到用户交互信息时,它会调用Presenter进行处理。也就是说,View层不包括任何的业务逻辑代码,它只会将交互交给Presenter,并从Presenter中接受结果来更新自己。
而MVP中的Presenter则负责业务逻辑,它是View和Model的桥接。它会根据View中的交互修改Model,或根据Model的变化来修改View。
注意:MVP中,因为View持有Presenter,所以Presenter中的View应该声明为weak或unowned,以避免循环引用。
优点:MVP相比MVC,耦合度大大降低,代码分配更加合理,测试起来非常方便,整个架构的理解和上手难度也不高。
缺点:MVP中,View的所有交互都要传递给Presenter处理,从而一旦项目功能增加了,View的代码和Presenter的代码都会增加。相比MVC在ViewController一个文件里面直接解决,MVP的总代码量可能会翻倍,这样App的维护成本和文件都会增大。
MVVM中的ViewModel的作用是什么
视图层的真正数据提供者。一般视图层展示的数据经常是一个或是多个模型的属性组合。简单来说,ViewModel就是为了视图展示而对模型层的数据进行包装。
视图层的交互响应者。所有用户的交互都会传递给ViewModel。ViewModel会依次更新视图层需要的属性,同时相应修改模型层的数据。这里依靠的是属性观察或响应式架构。
注意,在ViewModel类中,绝对不能包含视图层的任何类或结构体。
比较MVC、MVP和MVVM这三种架构
模型层几乎相同
-
视图层在理论上都被设计为被动,但是实际上略有不同。
从理论上来说,MVC希望视图层就是单纯的UIView,或者UIViewController,只负责UI的更新和交互,不涉及业务逻辑和模型更新。
MVP的视图层是完全被动的,单纯地把交互和更新传递给中间层。
MVVM的视图层并不是完全被动的,它会监视中间层的变化,一旦产生变化,则视图层也会相应变化。
-
中间层的设计师三种架构的核心差异。从逻辑上来说,中间层的作用就是连接视图层和模型层。它用于处理交互、接受通知和完成数据的更新。
MVC的耦合度喝高,代码分配最不合理,维护和扩展的成本最高。但是因为无须层级传递,所以代码总量最少,适合理解和应用。
MVP和MVVM相似,耦合度和代码分配都比较合理,比较容易实现高测试覆盖率。MVP的缺点是视图层需要将所有的交互传递给中间层,且要手动实现响应和更新,所以代码量远超MVVM。MVVM在响应和更新上,通过响应式框架自动操作,大大精简了代码量;但是需要引入第三方响应式框架,同时因为属性观察环环相扣,调用栈很大,导致DeBug起来尤其痛苦。
MVC、MVP和MVVM都是以试图为驱动的架构,三种皆以为用户交互和试图更新为主要服务目标。它们共同的缺点是没有涉及界面之间的跳转,即路由的设计。
VIPER之间的各个组件是如何交互的
视图层(View):与MVP或者MVVM的视图层类似。它包含于UI相关的一切操作。它接收用户的交互信息但并不处理,而是传递给展示层(Presenter)。
展示层(Presenter):与MVP的Presenter或是MVVM的ViewModel功能类似。Presenter在这里只响应并处理视图层传递过来的交互请求,并不直接对数据源进行修改,这是与MVX中间层最大的不同。若要修改数据,展示层会向其持有的数据管理层(Interactor)发送请求,Interactor会处理一切有关数据源的操作。此外,它还连接了路由层。
路由层(Router):专门负责界面跳转和组件之间的切换。当App占用的空间较少时,Router负责界面跳转。当App占用空间比较大时,不同的功能也业务会被拆分成不同的模块或组件,Router的作用就是在不同的组件之间进行连接。这是MVX架构所忽略的部分。
数据管理层(Interactor):专门负责处理数据源信息。包括网络请求、数据传输、缓存、存储、生成实例等操作。实际上,之前中间层和模型层的一些逻辑被进一步剥离至此,整个架构的逻辑也显得更加清晰。
模型层(Entity):只拥有初始化方法和属性相关的set/get方法,与之前的Model层大同小异。
优点:分工明确,VIPER层在代码分配、测试覆盖率上未所有架构之上。
缺点:与MVX架构一样,是一个视图驱动的架构。同时,VIPER由于分工精细,不同层级之间交互的代码很多,总体代码量很大,不适宜用在小型App中。