本文将回答知乎上iOS面试问题链接:如何面试 iOS 工程师?
Model层:
数据持久化存储方案有哪些?沙盒的目录结构是怎样的?各自一般用于什么场合?SQL语句问题:inner join、left join、right join的区别是什么?sqlite的优化网络通信用过哪些方式(100%的人说了AFNetworking...)如何处理多个网络请求并发的情况
在网络请求中如何提高性能
在网络请求中如何保证安全性
语言与基础知识:
内存中的栈和堆的区别是什么?那些数据在栈上,哪些在堆上?#define和const定义的变量,有什么区别什么情况下会出现内存的循环引用block中的weak self,是任何时候都需要加的么?GCD的queue,main queue中执行的代码,一定是在main thread么?
NSOperationQueue有哪些使用方式
NSThread中的Runloop的作用,如何使用?
.h文件中的变量,外部可以直接访问么?(注意是变量,不是property)讲述一下runtime的概念,message send如果寻找不到相应的对象,会如何进行后续处理 ?TCP和UDP的区别是什么?
MD5和Base64的区别是什么,各自场景是什么?
二叉搜索树的概念,时间复杂度多少?
架构:(我们招的不是架构师,这方面问的不多,而且从之前对APP的架构介绍里可以边听边问)
哪些类不适合使用单例模式?即使他们在周期中只会出现一次。
Notification的使用场景是什么?同步还是异步?
简单介绍一下KVC和KVO,他们都可以应用在哪些场景?
APP相关:
如何添加一个自定义字体到工程中
如何制作一个静态库/动态库,他们的区别是什么?
Configuration中,debug和release的区别是什么?
简单介绍下发送系统消息的机制(APNS) UI:
系统如何寻找到需要响应用户操作的那个Responder
多屏幕尺寸的适配
UIButton的父类是什么?UILabel呢?
push view controller 和 present view controller的区别
描述下tableview cell的重用机制
UIView的frame和bounds的区别是什么
最后是几道场景题,也是我最喜欢问的:
发送10个网络请求,然后再接收到所有回应之后执行后续操作,如何实现?
实现一个第三方控件,可以在任何时候出现在APP界面最上层
实现一个最简单的点击拖拽功能。上面那个拖拽之外,如果在手放开时,需要根据速度往前滑动呢?
如何减小一个应用程序的尺寸?
如何提高一个性用程序的性能?
不同版本的APP,数据库结构变化了,如何处理?
作者:张之诚
链接:https://www.zhihu.com/question/19604641/answer/56306604
来源:知乎
下面是给出的答案,有不对的地方还请多多指教!
一. 数据持久化存储方案有哪些?
- 属性列表(NSUserdefaults)
NSUserdefaults适合存储轻量级,弱业务相关的数据,存储在应用程序的一个plist文件里,Document目录平级的/Library/Prefereces里。- NSCoding
- 文件存储(NSFileManager)
- NSKeydeArchive归档存储
又名序列化,把对象转为字节码,通过key-value以文件的形式加密存储在磁盘上- 数据库
SQlite库提供了三种方式:Single Thread, Multi Thread, Serialized(默认)
Serialized串行队列执行所有操作,- Keychain
苹果提供的带有可逆加密的存储机制,存储密码和用户唯一标识串。
二. 沙盒的目录结构是怎样的?各自一般用于什么场合?
在应用的安装过程中,系统为每个单独的应用程序生成它的主目录和一些关键的子目录。苹果对沙盒的几天限制:
- 应用程序在自己的沙盒中运作,不能访问任何其他应用程序的沙盒;
- 应用之间不能共享数据;
- 苹果禁止任何读写沙盒以外的文件,禁止应用程序将内容写到沙盒以外的文件中;
- 沙盒目录里有三个文件夹:Documents、Library、Temp、AppName.app目录
Documents-----存储;应用程序的数据文件,存储用户数据或其他定期备份的信息;
library下有两个文件夹:Caches存储应用程序再次启动所需的信息,
Preferences包含应用程序的偏好设置文件,不可在这更改偏好设置;
temp存储临时文件- 获取沙盒根目录方法:NSHomeDirectory获取
获取Document路径
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).
三. SQL语句问题:inner join、left join、right join的区别是什么?
left join(左联接)返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接)返回包含右表中的所有记录和左表中联接字段相等的记录
inner join (等值联接) 只返回两个表中联接字段相等的行
select *from A left join B on A.id = B.id
四. 在网络请求中如何提高性能?
1.减少请求宽带:(压缩请求与响应)
(1)使用JSON数据,高效的数据交换格式
(2)Base64格式上传文件,对Base64数据进行压缩,减少30%的数据体积
2.降低请求延迟:(管道化请求)
(1)降低多个请求的延迟
(2)在单个TCP连接上发送HTTP请求,以管道的形式发送HTTP请求,从而优化全双工TCP连接的使用。
(3)Apache和IIS都支持管道,无需任何额外的配置。
3.避免网络请求
(1)在本地缓存内容避免不必要的网络流量
五. 在网络请求中如何保证安全性
https 和 加密
六. 内存中的栈和堆的区别是什么?那些数据在栈上,哪些在堆上?
栈区是由编译器自动分配释放,存放方法(函数)的参数值,局部变量的值等,栈是向低地址扩展的数据结构,是一块连续的内存区域。栈顶的地址和栈的最大容量是系统预先规定好的。
堆由程序员分配释放,若不释放,程序结束时由OS回收,向高地址扩展的数据结构,是不连续的内存区域,
OC对象放在堆内,非OC对象一般放在栈里面
栈是吃了吐,堆是吃了拉
链接:iOS 堆和栈的区别?
七. #define和const定义的变量,有什么区别:
1.#define在预处理阶段进行简单的替换,const在编译阶段使用;
2.#define不做类型检查,只是简单的替换,const有数据类型会执
行类型检查;
3.#define不能调试, const可以调试
4.#define大量的宏造成编译时间久
5.#define可以定义一些简单的函数,const不可以
6.const修饰右边的变量只读 NSString * const p
static作用:
- 修饰局部变量
(1)延长局部变量的生命周期,程序结束才会销毁
(2)局部变量只会生成一份内存,只会初始化一次- 修饰全局变量:只能在本文件中访问,修改全局变量的作用域,生命周期不会改
extern作用:声明全局变量
声明一个变量,不能定义变量
static和const联合使用:声明一个静态全局只读变量
static NSString * const name = @"name";
extern与const联合使用
当有多个文件使用同一个字符串常量时,可以使用extern和const组合
新建一个全局的文件管理全局的变量
在.m里定义 NSString *const YYTextViedBegin = @"YYTextVditing";
在.h里应用 UIKIT_EXTERN NSString *const YYTextViewTextDidBegin;
八. 什么情况下会出现内存的循环引用
block,A强引用了block,block有强引用了A
九. block中的weak self,是任何时候都需要加的么?
只有产生循环引用了才会加
十. GCD的queue,main queue中执行的代码,一定是在main thread么?
对于queue中所执行的代码不一定在main thread中。如果
queue是在主线程中创建的,那么所执行的代码就是在主线程中执行。如果是在子线程中创建的,那么就不会在main thread中执行。
对于main queue就是在主线程中的,因此一定会在主线程中执行。获取 main queue就可以了,不需要我们创建,获取方式通过调用方法 dispatchgetmain_queue来获取。
链接:多线程
十一. NSOperationQueue有哪些使用方式
与GCD的区别
- GCD使用方便,代码逻辑清晰,
- GCD是C语言构成的API,NSOperationQueue是基于GCD的Objective-C对象的封装,提供了更多的选择
- NSOperationQueue任务可以很方便的取消未执行的任务
- GCD只支持FIFO队列,NSOperation可以通过依赖关系设置操作执行顺序,控制在特定的任务执行完后才执行,GCD可以通过barrier或者group控制执行顺序,但是依赖关系复杂时,代码逻辑太复杂
- NSOperation支持KVO监听任务的状态(完成、执行中、取消等状态)
- NSOperation可以设置同一队列中任务的优先级,GCD只能区分不同队列的优先级
- 自定义NSOperation,封装任务逻辑,提高整个代码的复用度
十二. NSThread中的Runloop的作用,如何使用
runloop是一个线程里运行循环,并对收到的事件进行处理,
- 保持线程长时间存活
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
[runLoop run];- 用于定时器
十三. .h文件中的变量,外部可以直接访问么?(注意是变量,不是property)
成员变量三中权限:@private, @protected, @public
不可以,.h中的成员变量默认是@protected权限,子类可以访问,可以做权限修改,外部调用会报protected错误
十四. 讲述一下runtime的概念,message send如果寻找不到相应的对象,会如何进行后续处理 ?
objc向一个对象发送消息,runtime会根据对象的isa指针找到该对象实际所属类,然后在该类方法列表以及父类的方法列表中查找方法实现,如果向一个nil对象发送消息,在查找isa指针时就是0地址返回,不会出错
十五. TCP和UDP的区别是什么?
TCP:面向连接、传输可靠(保证数据正确性,数据顺序)、用于传输大量数据(流模式)、速度慢、建立连接需要开销较多(时间,系统资源)。
UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快。
TCP在连接持续过程中,socket收到的数据是同一台主机发出。
UDP只要知道接收端的IP和端口,网络可达,任何主机都可以向接受端发送数据
十六. MD5和Base64的区别是什么,各自场景是什么
MD5是用哈希算法(摘要算法)加密的:不可逆;密文总是16进制32位
一般用于文件校验、密码存储
Base64是公开的代码加密,url加密
十七. 二叉搜索树的概念,时间复杂度多少
采用二叉树链表作为存储结构,每个左节点均小于父节点,每个右节点均大于父节点
时间复杂度:O(log2(n))
十八. 哪些类不适合使用单例模式?即使他们在周期中只会出现一次。
同一类型的对象需要在不同的用例场景发生变化
十九. Notification的使用场景是什么?同步还是异步?
一对多,同步
二十. 简单介绍一下KVC和KVO,他们都可以应用在哪些场景?
KVO:键值监听,观察某一属性的方法
KVC:键值编码,间接访问对象的属性
二十一. 如何添加一个自定义字体到工程中
> 添加ttf文件,通过[UIFont fontWithName:@“字体名” size:20]使用
二十二. 如何制作一个静态库/动态库,他们的区别是什么?
静态库在编译时期:commond+shift+N(即新建工程)----->FrameWork&library—>Cocoa Touch Static Libiary
动态库在运行时期:commond+shift+N(即新建工程)----->FrameWork&library—>Cocoa Touch Libiary
区别:静态库:模块化,分工合作,避免少量改动,但是会导致大量的重复编译连接,也可以重用,注意不是共享使用 动态库:可将可执行的文件体积缩小,多个应用程序用同一份库文件,节省资源
二十三. Configuration中,debug和release的区别是什么?
debug :测试时期,调试版本,能设置断点,单步执行 release:发行版本,体积小,运行速度快
二十四. 简单介绍下发送系统消息的机制(APNs)
苹果的APNS不保证送达率,如你网络不好,在APNs那里下线,只保留最后一条消息,并告知推送数,其他消息会被APNs丢弃。
UI:
二十五. 系统如何寻找到需要响应用户操作的那个Responder
系统通过调用hitTest:withEvent:接受位置参数CGPoint,从底层按照subview的顺序,测试该点在哪个view上,如果在该view上,则继续测试是否在view的subview上。。。
响应链是从底层往上的,当没有可以处理的responder时按响应链往上回溯,一直到application也无人处理则丢弃
二十六. 多屏幕尺寸的适配
Autolayout、Masonry、SDAutoLayout、纯Frame计算比例
二十七. UIButton的父类是什么?UILabel呢?
UIControl UIView
二十八. push view controller 和 present view controller的区别
是否使用UINavigationController
二十九. 描述下tableview cell的重用机制
当cell被移出界面,进入重用池,当cell要显示时候从重用池中获取cell
三十. UIView的frame和bounds的区别是什么
Frame是参考父视图的坐标系,bounds参考自身坐标系
三十一. 发送10个网络请求,然后再接收到所有回应之后执行后续操作,如何实现?
GCD信号量,队列组
- 做法:通过dispatch_group_t来实现,将每个请求放入到Group中,将合并成大图的操作放在dispatch_group_notify中实现。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加载图片1 / });
dispatch_group_async(group, queue, ^{ /加载图片2 / });
dispatch_group_async(group, queue, ^{ /加载图片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并图片
});- dispatch_group_enter()和dispatch_group_leave()
三十二. 实现一个第三方控件,可以在任何时候出现在APP界面最上层
三十三. 实现一个最简单的点击拖拽功能。
三十四. 上面那个拖拽之外,如果在手放开时,需要根据速度往前滑动呢?
三十五. 如何减小一个应用程序的尺寸?
链接:减小iOS App或者静态库体积
链接: iOS App 瘦身 - 以 Swift App Yep 为例
三十六. 如何提高一个应用程序的性能?
链接:25条提高iOS App性能的建议和技巧
三十七. 不同版本的APP,数据库结构变化了,如何处理?
数据库迁移
- 运行APP后,判断是否存在数据库,不存在则直接创建数据库,若存在取出数据库版本号进行case处理
- 第一次运行APP,第一次建立版本库,将数据信息存入数据库,同时保存一个当前版本加一的字段到数据库中
- 当APP更新时,从数据库中取出数据库中保存的版本字段,然后case执行相关方法,修改完数据结构以后,再一次将版本字段加一存到数据库中。
链接:App升级时数据库的迁移更新