iOS面试题锦集(三)——Foundation

1. KVO内部实现原理?

  • KVO是基于runtime机制实现的;
  • 当某个类的对象第一次被观察时,系统就会在运动期动态的创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法;派生类在被重写的setter方法实现真正的通信机制(Person -> NSKVONotifying Person)。

2. 是否可以把比较耗时的操作放在NSNotificationCenter中?

  • 如果在异步线程发的通知,那么可以执行比较耗时的操作
  • 如果在主线程发的通知,那么就不可以执行比较耗时的操作

3. Foundation对象与Core Foundation对象有什么区别?

  • Foundation对象是OC的,Core Foundation对象是C的;
  • 数据之间的转换:
    • ARC:__bridge_retained、__bridge_transfer
    • 非ARC:__bridge

4. 不用中间变量,用两种方法交换A和B的值。

A=A+B
B=A-B
A=A-B

或者

A=A^B
B=A^B
A=A^B

5. 简单描述下对单例模式设计的理解?

  • 节省内存资源,一个应用就一个对象

6. 什么是动态,举例说明

  • 在程序运行过程中才执行的操作

7. runtime实现的机制是什么?怎么用?一般用于干嘛,你还能记得你所使用的相关的头文件或者某些方法的名称吗?

运行时机制,runtime库里面包含了跟类、成员变量、方法相关的API,比如获取类里面的所有成员变量,为类动态的添加成员变量,动态改变类的方法实现,为类动态添加新的方法等,需要导入

  • runtime:运行时机制,是一套C语言库
  • 实际上我们编写所有OC代码,最终都是转换成了runtime库里的东西,比如类转换成了runtime库里面的结构体等数据类型,方法转换成了runtime库里面的C语言函数,平时调用方法都是转换成了objc_msgSend函数(所以OC有个消息发送机制)。
  • 因此,可以说runtime是OC的底层实现,是OC的幕后执行者
  • 有了runtime库,能做什么事情呢?runtime库里面包含了跟类、成员变量、方法相关的API,比如获取类里面的所有成员变量,为类动态添加成员变量,动态改变类的方法实现,为类动态添加新的方法等
  • 因此,有了runtime,想改什么就改什么

8. 是否使用Core Text 或者Core Image等?如果使用过,请谈谈您使用Core Text或者Core Image的体验。

  • Core Text

    • 随意修改文本的样式
    • 图文混排(纯C语言)
    • 国外:Niumb
  • Core Image

    • 滤镜处理
    • 能调节图片的各种属性(对比度、色温、色差等)

9. NSNotification和KVO的区别和用法是什么?什么时候应该使用通知,什么时候应该使用KVO,它们的实现上有什么区别吗?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果可能,会有什么潜在的问题?如果不能,为什么?(虽然protocol和delegate这种东西面试已经面烂了)

  • 通知比较灵活(一个通知能被多个对象接收,一个对象能接收多个通知);
  • KVO性能不好(底层会动态产生新的类),只能监听某个对象属性的改变,不推荐使用(一个对象属性能被多个对象监听,一个对象能监听多个对象的其他属性);
  • 代理比较规范,但是代码多(默认是一对一)。

详情参考: IOS 如何选择delegate、notification、KVO?

10.Block内部的实现原理

  • Objective-C是对C语言的扩展,block的实现是基于指针和函数指针。

11. 有两个数组a,b,大小都为n,数组元素的值任意,无序。(要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小)

思路:通过交换的方式,最终的状态是在保证两个序列中元素个数相同的条件下,任何一个元素都可以位于两个序列中的任何一个。这样问题可以转化为:在一个长度为2*n的整数序列中,如何将元素个数分成两个子集,记每个子集的元素之和分别为S1和S2,使得|S1-S2|最小。显然这是一个最优化问题,如果用brute-force方法,组合数是C(2n,n)=(2n)!/(2*(n!)), 如果n很大这个方法不奏效。

采用回溯法(backtracking),即前序(preorder)遍历状态空间树(state-space tree)。难点在于剪枝条件的确定,下面说明如何确定剪枝条件:

注意到如果将原序列按从小到大的顺序排好序,每次从较大的元素开始取,可以得到一个这样的规律:设长度为2*n序列的元素总和为Sigma,当前集合元素的和为S,剩下的元素之和为Sigma-S,如果二者满足S>=Sigma-S,即Sigma<=2*S,那么在当前集合中剩下需要添加进来的元素必须从余下的元素中取最小的那些元素,这样才能保证|S1-S2|最小。这是因为如果在下一次任意从余下的元素中取的元素分别为e和f,那么取e后的两个子集差为(S+e) - (Sigma-S-e) = 2S-Sigma +2e,取f后的两个子集差为2S-Sigma +2f,显然如果e>f>0, 则有前者的子集差大于后者的子集差(注意这里假设元素都为非负整数,原序列中有负数的情况参考下面的讨论)。

如果输入序列中有负整数,可以通过平移操作转化为非负,因为每个数都平移了,它们的差值保值不变。如果不平移,结果不一定正确,比如:输入的2*n序列为:-10,5,3,20,25,50,平衡的对半子集应该为[-10,5,50]和[3,20,25],差值的绝对值为3。在下面的实现中,如果不考虑平移,得到的错误结果却是[-10,3,50]和[5,20,25],差值的绝对值为7。
另外在状态空间树只需要考虑根节点的左枝子树,因为原问题考虑的是对半子集。

详情参考:用回溯法(backtracking)解决平衡集合问题(一道微软公司面试题)

你可能感兴趣的:(iOS面试题锦集(三)——Foundation)