面试题整理

虽然本人两年多菜鸟,不过最近由于部门业务需求也开始做一些iOS方面的面试,遇到过三两句说不上来的,也遇到过说的停不下来的。映像最深的有一个面试者本身水平应该不错,但是过于自我感觉良好,但每次答非所问老是岔开话题的。果断PASS..所以也有些感触,面试不管水平如何,一个好的态度还是必须的。

作为面试官出从来就不是为了难倒面试者,而是为了多角度全面的了解面试者从而建立信任。

下面是从标哥博客(http://www.huangyibiao.com/archives/451)收集的一下面试题,在这里也是自己的一次学习记录:

1、iOS数据持久化存储方案有哪些?
参考答案:

plist属性列表存储(如NSUserDefaults)
文件存储(如二进制数据写入文件存储,通过NSFileManager来操作将下载起来的二进制数据写一篇文件中存储)
NSKeydeArchiver归档存储,常见的是自动化归档/解档处理,想要学习如何通过runtime实现自动化归档/解档,可以阅读文章:学习通过runtime实现自动化归档/解档
数据库SQLite3存储(如FMDB、Core Data)
2、沙盒的目录结构是怎样的?各自一般用于什么场合?
参考答案:

Application:存放程序源文件,上架前经过数字签名,上架后不可修改
Documents: 保存应⽤运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
tmp: 保存应⽤运行时所需的临时数据,使⽤完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时 不会备份该目录
Library/Caches: 保存应用运行时⽣成的需要持久化的数据,iTunes同步设备时不会备份该目录。⼀般存储体积大、不需要备份的非重要数据,比如网络数据缓存存储到Caches下
Library/Preference: 保存应用的所有偏好设置,如iOS的Settings(设置) 应⽤会在该目录中查找应⽤的设置信息。iTunes同步设备时会备份该目录

3、#define定义的宏和const定义的常量有什么区别?
参考答案:

define定义宏的指令,程序在预处理阶段将用#define所定义的内容只是进行了替换。因此程序运行时,常量表中并没有用#define所定义的宏,系统并不为它分配内存,而且在编译时不会检查数据类型,出错的概率要大一些。
const定义的常量,在程序运行时是存放在常量表中,系统会为它分配内存,而且在编译时会进行类型检查。
define定义表达式时要注意“边缘效应”,例如如下定义:

#define N 2 + 3 // 我们预想的N值是5,我们这样使用N
int a = N / 2;  // 我们预想的a的值是2.5,可实际上a的值是3.5

4、常见的出现内存循环引用的场景有哪些?
参考答案:

定时器(NSTimer):NSTimer经常会被作为某个类的成员变量,而NSTimer初始化时要指定self为target,容易造成循环引用(self->timer->self)。 另外,若timer一直处于validate的状态,则其引用计数将始终大于0,因此在不再使用定时器以后,应该先调用invalidate方法
block的使用:block在copy时都会对block内部用到的对象进行强引用(ARC)或者retainCount增1(非ARC)。在ARC与非ARC环境下对block使用不当都会引起循环引用问题, 一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.someBlock = Type var{[self dosomething];或者self.otherVar = XXX;或者_otherVar = …};出现循环的原因是:self->block->self或者self->block->_ivar(成员变量)
代理(delegate):在委托问题上出现循环引用问题已经是老生常谈了,规避该问题的杀手锏也是简单到哭,一字诀:声明delegate时请用assign(MRC)或者weak(ARC),千万别手贱玩一下retain或者strong,毕竟这基本逃不掉循环引用了!

5、block中的weak self,是任何时候都需要加的么?
参考答案:

不是什么任何时候都需要添加的,不过任何时候都添加似乎总是好的。只要出现像self->block->self.property/self->_ivar这样的结构链时,才会出现循环引用问题。好好分析一下,就可以推断出是否会有循环引用问题。

6、GCD的queue、main queue中执行的代码一定是在main thread么?
参考答案:

对于queue中所执行的代码不一定在main thread中。如果queue是在主线程中创建的,那么所执行的代码就是在主线程中执行。如果是在子线程中创建的,那么就不会在main thread中执行。
对于main queue就是在主线程中的,因此一定会在主线程中执行。获取main queue就可以了,不需要我们创建,获取方式通过调用方法dispatch_get_main_queue来获取。
7、头文件中声明的成员变量(不是属性),外部可直接访问么?
参考答案:

外部不能直接访问头文件所声明的成员变量,需要提供成员变量的getter方法才能在外部访问。而属性已经直接给我们自动生成了getter方法,因此外部可以直接访问属性。

8、TCP和UDP的区别是什么?
参考答案:

TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序传输)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。
UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快,传输的是报文。
3#9、MD5和Base64的区别是什么,各自使用场景是什么?
参考答案:

做过加密相关的功能的,几乎都会使用到MD5和Base64,它们两者在实际开发中是最常用的。

MD5:是一种不可逆的摘要算法,用于生成摘要,无法逆着破解得到原文。常用的是生成32位摘要,用于验证数据的有效性。比如,在网络请求接口中,通过将所有的参数生成摘要,客户端和服务端采用同样的规则生成摘要,这样可以防篡改。又如,下载文件时,通过生成文件的摘要,用于验证文件是否损坏。
Base64:属于加密算法,是可逆的,经过encode后,可以decode得到原文。在开发中,有的公司上传图片采用的是将图片转换成base64字符串,再上传。在做加密相关的功能时,通常会将数据进行base64加密/解密。

10、发送10个网络请求,然后再接收到所有回应之后执行后续操作,如何实现?
参考答案:

从题目分析可知,10个请求要全部完成后,才执行某一功能。比如,下载10图片后合成一张大图,就需要异步全部下载完成后,才能合并成大图。

做法:通过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(), ^{
    // 合并图片
});

11、__block和__weak修饰符的区别?
参考答案:

__block不管是ARC还是MRC模式下都可以使用,它的作用是标识block外的变量在block内使用可以修改变量的值。
__weak只能在ARC模式下使用,表示弱引用,主要是用于防止循环引用而引入的。
12、常见的Http状态码有哪些?
参考答案:

302是请求重定向。
500及以上是服务器错误,如503表示服务器找不到、3840表示服务器返回无效JSON。
400及以上是请求链接错误或者找不到服务器,如常见的404。
200及以上是正确,如常见的是200表示请求正常。
100及以上是请求接受成功。

13、iOS与H5交互的方式有哪些?
参考答案:

通过协议在shouldStartRequest中捕获请求,获取scheme来判断预先定义的功能,然后调用native代码。比如,定义点击图片调用native来展示大图,那么js接收到点击时,重定向将图片的url添加上自定义scheme,如HYBImagePreview://这样。更详细请阅读我的文章:Swift版、Objective-C版本
通过iOS7以后引入的JavascriptCore框架来实现,通过注入对象的方式来实现,维护起来更简单。更详细请阅读我的文章:Oc版本与JS交互、Swift版与JS交互、如何使用的是WKWebView,可阅读WKWebView与JS交互
通过WebViewJavascriptBridge这个第三方库来实现,具体百度吧!
14、如何浅拷贝与深拷贝的理解?
参考答案:

所谓浅拷贝是指只复制指向对象的指针,而不复制引用对象本身(同一份内存),而所谓深拷贝是指复制引用对象本身(新创建了一份内存)。

打个比方:

浅复制好比是你的影子,当你挂了,影子也消失了。 深复制好比克隆你,即使你挂了,新克隆出来的人是新的生命,不会因为你挂了也跟着挂。

15、什么是懒加载?常见场景?
参考答案:

所谓懒加载是指在真正需要到的时候才真正载入内存。常见的场景就是重写属性的getter方法,在getter方法中判断是否创建过或者加载过,若未创建过或者未加载过,则创建或者加载。

懒加载可以优化性能,有的功能一般情况下不会使用到,但是这些功能一使用就会占较大的内存,此时使用懒加载就非常的好了。

常见的写法:

- (NSMutableArray *)dataSource {
  if (_dataSource == nil) {
    _dataSource = [[NSMutableArray alloc] init];
    // 添加默认数据等
    // ...
  }
}

16、一个tableView是否可以关联两个不同的数据源?
参考答案:

当然是可以关联多个不同的数据源,但是不同同时使用多个数据源而已。比如,一个列表有两个筛选功能,一个是筛选城市,一个是筛选时间,那么这两个就是两个数据源了。当筛选城市时,就会使用城市数据源;当筛选时间时,就会使用时间数据源。

17、对象添加到通知中心中,当通知中心发通知时,这个对象却已经被释放了,可能会出现什么问题?
参考答案:

其实这种只是考查对通知的简单应用。通知是多对多的关系,主要使用场景是跨模块传值。当某对象加入到通知中心后,若在对象被销毁前不将该对象从通知中心中移除,当发送通知时,就会造成崩溃。这是很常见的。所以,在添加到通知中心后,一定要在释放前移除。

18、实现过框架或者库以供他人使用么?如果有,请谈一谈构建框架或者库时候的经验;如果没有,请设想和设计框架的public的API,并指出大概需要如何做、需要注意哪些问题,以使人人更容易地使用你的框架。
参考答案:

实现过的。本人开源过多个组件的,可到GITHUB查找:标哥的GITHUB开源组件库,每个开源组件都有详细的博文说明如何使用哦。当然,在公司也设计了相关的公共框架,不可公开!从以下角度出发来思考和设计公共框架:

确保外部调用简单,且保证有详细的头文件注释说明。
确保API编码规范,保证风格统一。
确保API易扩展,可以考虑预留参数
确保没有外部依赖或者依赖要尽可能的少,以保证公共库的纯洁(原则上不能有外部依赖)
确保易维护,不存在冗余API
19、如何自动计算cell的高度?
参考答案:

笔者喜欢纯代码自动布局,一直使用Masonry这个第三方库来实现纯代码自动布局的,使用起来非常简单,而且效率也很高。开发起来,提高了开发效率。

关于Masonry自动计算行高,笔者提供了swift版和oc版本的扩展,这两个版本都提供了自动计算行高的功能,并且带有缓存功能,保证永远只计算一次行高,效率就会很高,一般的应用也就不会卡屏了。

实现原理:通过数据模型的id作为key,以确保唯一,如何才能保证复用cell时不会出现混乱。在配置完数据后,通过更新约束,得到最后一个控件的frame,就只可以判断cell实际需要的高度,并且缓存下来,下次再获取时,判断是否存在,若存在则直接返回。因此,只会计算一遍。

20、UITableView是如何计算内容高度的?为什么初始化配置数据时,获取行高的代理方法会调用数据条数次?
参考答案:

UITableView是继承于UIScrollView的,因此也有contentSize。要得到tableview的contentsize,就需要得到所有cell的高度,从而计算出总高度,才能得到contentsize。因此,在reloadData时,就会调用该代理方法数据条数次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值