iOS面试集锦

  1. 写出中序遍历二叉树的算法(不限编程语言,已知 TreeRoot 为二叉树的根)
  2. 快速排序,归并排序的时间复杂度分别是什么?
  3. UITableViewCell 可重用机制是什么?有什么要注意的地方?
  4. OC 中的 @property 和 @synthesize 有什么用?
  5. OC有没有重载(overload)和重写(overwrite)?有的话请举个例子。
  6. 列举出 iOS 开发中三种最常出现的循环引用的场景。
  7. MVC 模式具体指什么?iOS现在流行的 MVVM 是什么?
  8. iOS 中设计金钱计算和比较时,用数值应该用哪个类型来处理?
  9. iOS 上触摸事件的视图检测和事件传递过程是怎样的?
  10. 写一个折半查找或者快速排序

  1. 对内存管理的理解。
  2. 类别的作用
  3. 点击屏幕上的按钮,系统做了什么?

  1. id 和 instancetype 的区别?
  2. 有方法查看当前系统内存使用的情况吗?
  3. 为什么 NotificationCenter 要 removeObserver? 如何实现自动 remove?
  4. iOS的沙盒目录结构是怎样的? App Bundle 里面都有什么?
  5. 简单描述一下客户端的缓存机制?

  1. 按钮或者 View 在进行 frame 动画的时候,为什么点击 frame 动画的重点位置才能触发事件?如何在动画过程中响应事件?

#####1.写出中序遍历二叉树的算法(不限编程语言,已知 TreeRoot 为二叉树的根)。

最近在学 Python 所以整理了一个 Python 版的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# -- coding: UTF-8 --
# @Date : 2017-02-21
# @Author : CepheusSun
# @Version : python 2.7
class Tree(object):
def __init__(self, data, left, right):
self.data = data
self.left = left
self.right = right
def in_visit(tree):
"""递归中序遍历"""
if tree:
in_visit(tree.left)
print (tree.data)
in_visit(tree.right)
def in_stack(tree):
"""堆栈中序遍历"""
if tree:
my_stack = []
node = tree
while node or my_stack:
while node:
my_stack.append(node)
node = node.left
node = my_stack.pop()
print(node.data)
node = node.right
print my_stack2.pop().data
def main():
node1 = Tree(1, 0, 0)
node2 = Tree(2, 0, 0)
node3 = Tree(3, node1, node2)
node4 = Tree(4, 0, 0)
node5 = Tree(5, node4, node3)
print("the post_visit is ...")
post_visit(node5)
post_stack(node5)
print("the pre_visit is ...")
pre_visit(node5)
pre_stack(node5)
print("the in_visit is ...")
in_visit(node5)
in_stack(node5)
if __name__ == '__main__':
main()
2.快速排序,归并排序的时间复杂度分别是什么?
  • 归并排序:稳定,时间复杂度 O(nlog n)
  • 快速排序:不稳定,时间复杂度 最理想 O(nlogn) 最差时间O(n^2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# -- coding: UTF-8 --
# @Date : 2017-02-21
# @Author : CepheusSun
# @Version : python 2.7
def quick_sort(array, first_index, last_index):
"""快速排序"""
if first_index < last_index:
div_index = partition(array, first_index, last_index)
quick_sort(array, first_index, div_index)
quick_sort(array, div_index + 1, last_index)
else:
return
def partition(array, first_index, last_index):
i = first_index - 1
for j in range(first_index, last_index):
if array[j] <= array[last_index]:
i += 1
array[i], array[j] = array[j], array[i]
array[i + 1], array[last_index] = array[last_index], array[i + 1]
return i
def merge(left, right):
i, j = 0, 0
res = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
res.append(left[i])
i += 1
else:
res.append(right[j])
j += 1
res += left[i:]
res += right[j:]
return res
def merge_sort(array):
"""并归排序"""
if len(array) <= 1:
return array
num = len(array) / 2
left = merge_sort(array[:num])
right = merge_sort(array[num:])
return merge(left, right)
def main():
array = [1, 4, 7, 5, 4, 9, 5, 8, 76, 5, 567, 54, 6, 54, 5, 5, 4, 0]
print("initial array : \n", array)
print("merge sort result: \n", merge_sort(array))
quick_sort(array, 0, len(array) - 1)
print("result array :\n", array)
if __name__ == '__main__':
main()
```
##### 3.UITableViewCell 可重用机制是什么?有什么要注意的地方?
UITableView 通过重用单元格来达到节省内存的目的:通过为每个单元格指定一个重用标识符,即指定来单元格的种类,以及当单元格滚出屏幕时,允许恢复单元格以便重用。对于不同种类的单元格使用不同的ID,对于简单的表格,一个标识符就够了。
需要注意的地方:
取出来的cell是有可能已经捆绑过数据或者加过子视图的,如果有必要,要清除需要用与显示的数据和remove掉add过的子视图。不然会造成错乱的情况
##### 4.OC 中的 @property 和 @synthesize 有什么用?
答:
iOS2.0 Apple引入 `@property` `@synthesize` 概念,两者组合使用可以自动生成 `seter` `geter`方法,iOS4.0以后只需要 `@property` 即可实现 自动生成 `seter` `geter`方法。

//setter

  • (void)setObjStr:(NSString *)objStr {
    if (_ objStr != objStr) {
    [_objStr release];
     _objStr = [objStr copy];
    
    }
    }
    //getter
  • (NSString *)objStr {
    return _objStr;
    }
    1
    以上代码等价于

//iOS2.0-iOS4.0
@interface Obj : NSObject
@property (nonatomic, copy) NSString *objStr;
@end

@implementation Object
@synthesize objStr = _ objStr;
@end

1
iOS4.0及以后

@interface Obj : NSObject
@property (nonatomic, copy) NSString *objStr;
@end

1
2
3
4
5
6
7
8
9
10
11
##### 5.OC有没有重载(overload)和重写(overwrite)?有的话请举个例子。
答:
OC 语言不支持重载
```oc
-(void)doSth:(NSInteger)integer;
-(void)doSth:(NSString *)str;
//对Objective-C而言是一样的,都叫方法doSth: 同时定义实现这两个方法是无法编译通过的。 若要达到类似重载的效果,可以这样跟不同参数:
-(void)doSthWithInt:(NSInteger)integer;
-(void)doSthWithStr:(NSString *)str

OC 支持重写

子类重写父类方法
6.列举出 iOS 开发中三种最常出现的循环引用的场景。

答:

  • delegate 被强引用了,代理应该使用 weak (拓展 weak 干了啥使自己可以防止循环的出现)
  • 使用 block 的时候,注意是否出现互相持有。
  • 使用 NSTimer 的时候,我们可能会使+scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 这个方法在建立的时候需要指定 target ,会被 timer retain 一份。因为 timer 还在一直执行,就会导致无法运行 dealloc的方法。正确的做法是在 viewDidDisappear: 的时候停止timer
7.MVC 模式具体指什么?iOS现在流行的 MVVM 是什么?

MVC:

MVC模式考虑三种对象:模型对象、视图对象和控制器对象。 模型对象负责应用程序的数据和定义操作数据的逻辑; 视图对象知道如何显示应用程序的模型数据; 控制器对象是M与V之间的协调者。

MVVM:

MVVM 把 View Controller 作为 View
View 和 Model 之间没有紧耦合
MVVM 是在 View 和 ViewModel 之间进行绑定。
什么是 ViewModel 呢?基本上来说,它是 View 和 View 状态的独立于     UIKit 外的一个呈现,ViewModel 调用 Model 中的的变化,根据 Model 的变化进行调整,并且通过 View 和 ViewModel 的绑定,同步调整 View。
8.iOS 中设计金钱计算和比较时,用数值应该用哪个类型来处理?
  • NSDecimalNumber 继承于 NSNumber
  • NSDecimalNumber 包含 加减乘除,幂运算,指数运算,四舍五入,比较运算。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    /** 示例 比较运算
    NSOrderedAscending 升序
    NSOrderedSame 相等
    NSOrderedDescending 降序
    - (NSComparisonResult)compare:(NSNumber *)decimalNumber;
    */
    NSDecimalNumber *discount1 = [NSDecimalNumber decimalNumberWithString:@"1.2"];
    NSDecimalNumber *discount2 = [NSDecimalNumber decimalNumberWithString:@"1.3"];
    NSComparisonResult result = [discount1 compare:discount2];
    if (result == NSOrderedAscending) {
    NSLog(@"1.2 < 1.3");
    } else if (result == NSOrderedSame) {
    NSLog(@"1.2 == 1.3");
    } else if (result == NSOrderedDescending) {
    NSLog(@"1.2 > 1.3");
    }
    输出结果 1.2 < 1.3
    ```
    ##### 9.iOS 上触摸事件的视图检测和事件传递过程是怎样的?
    * 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中。(为什么是队列而不是栈?因为队列的特定是先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列。)
    * UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
    * 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。
    找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。
    ##### 10.写一个折半查找或者快速排序
    用 Python写了一个二分查找
    ```python
    # -- coding: UTF-8 --
    # @Date : 2017-02-21
    # @Author : CepheusSun
    # @Version : python 2.7
    def in_bisect(word_list, word):
    """check weather a word is in a list using bisection search.
    Precondition: the words in the list are sorted
    :param word_list: list of strings
    :param word: string
    """
    if len(word_list) == 0:
    return False
    # 结果四舍五入
    i = len(word_list) // 2
    if word_list[i] == word:
    return True
    if word_list[i] > word:
    # search the first half
    return in_bisect(word_list[:i], word)
    else:
    # search the second half
    return in_bisect(word_list[i + 1:], word)
    def main():
    array = ['1', 'asf', 'sf2', 'df1', '2das', '5asdfd', '0sadf',
    '2ge', 'efc2', '1eds', '2ef', '2apple', 'apple', 'orange']
    print(in_bisect(array, '1'))
    print(in_bisect(array, '111'))
    if __name__ == '__main__':
    main()

Question 1.内存管理理解不正确的是
A 程序A里有一段内存被成功申请完成之后,内存计数器就从0变为1 (这个过程是alloc);
B 程序B里要使用已存在内存,那么内存计数器从1变为2 (这个过程是retain或者copy);
C 紧接着程序A不需要这个内存了,那么程序A就把这个内存计数器减1 (这个过程是release);
D 当系统发现这个内存计数器变为小于等于0,那么就调用垃圾回收程序把这段内存回收(这个过程是dealloc);

Question 2.类别的作用不正确的是
A 将类的实现分散到多个不同文件或多个不同框架中。
B 创建对私有方法的前向引用。
C 向对象添加非正式协议。 继承可以增加,修改或者删除方法。
D 不能添加属性

Question 3.点击屏幕上的按钮,系统做了什么?

Answer

Answer 1. B 使用并不会增加引用技术,持有才会。
Answer 2. D

class-continuation分类是唯一能给添加属性的分类。而所谓的关联对象,我在52个方法中找到了这样一句话:
    * “定义关联对象时可以指定内存管理语义,用以模仿定义属性时所采用的“拥有”和“非拥有关系””
可见,关连对象跟属性是两个不同的概念,不能混为一谈。

类别的三个作用:
    * 可以将类的实现分散到多个不同文件或多个不同框架中,方便代码管理。
    * 创建对私有方法的前向引用。比如在别人的类或者框架中有一个方法的是没有说明的,这时候通过类别声明这个方法,就可以在不破坏原来框架的情况下,使用到这个方法。
    * 向对象添加非正式协议。

Answer 3.

  • 触摸事件的传递是从父空间传递到子控件,如果父空间不能接收触摸事件,那么子控件就不能接收触摸事件
  • 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中。(为什么是队列而不是栈?因为队列的特定是先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列。)
  • UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
  • 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。
    找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。

#####1. id 和 instancetype 的区别?

  1. 在 ARC(Auto Reference Count)环境下:

    instancetype 用来在编译期确定实例的类型,而使用 id 的话,编译器不检查类型, 运行时检查类型.
    

    在 MRC(Manual Reference Count)环境下:

    instancetype 和 id 一样,不做具体类型检查
    
  2. id 可以作为方法的参数,但 instancetype 不可以
    instancetype 只适用于初始化方法和便利构造器的返回值类型

#####2. 有方法查看当前系统内存使用的情况吗?
一般我上手新的项目,检查内存问题三部曲;

  1. 静态分析, 初步了解代码不规范的地方,或者是一些内存泄漏问题。
  2. instrument, 初略的定位我们在什么地方发生了内存泄漏。
  3. 代码测试内存泄漏, 观察对象的生成和销毁是否配对。使用MLeaksFinder来配合检查
  • 两个对象互相持有,主要是 delegate 使用 weak 属性, 在 iOS8 上系统 delegate 使用的是 assign 这会带来一些问题,踩过坑的老司机可以提一下。
  • 有时候需要使用 removeFromSuperView 来释放, a 持有了 b , b 被 add 到了 c 上, 而在程序设计中b 的生命周期应该和 a 相同, 这个时候使用 self.b = nil 并不会吧 b 释放, 我们应该在之前加上 [b removeFromSuperView]

#####3. 为什么 NotificationCenter 要 removeObserver? 如何实现自动 remove?

  • 如果不移除,万一注册通知的类被销毁之后又发送了通知, 程序会 crash。 因为野指针。
  • 实现自动 remove;通过自动释放机制, 通过动态属性将 remove 转移给第三者,解除耦合,达到自动实现 remove 。

#####4. iOS的沙盒目录结构是怎样的? App Bundle 里面都有什么?

  1. 沙盒结构
    • Application: 存放程序源文件, 上架前经过数字签名,上架后不可修改
    • Documents: 常用目录, iCloud 备份目录,存放数据,这里不能缓存文件,否则上架不被通过
    • Library
      • Caches: 存放体积大又不需要备份的数据, SDWebImage 就是这个
      • Preference: 设置目录, iCloud会备份设置信息。
      • tmp: 存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除
  2. App Bundle
    • Info.plist: 文件的配置信息,系统依赖次文件获取应用程序的相关信息。
    • 可执行文件: 此文件包含应用程序的入口和通过静态链接到应用程序 target 的代码。
    • 资源文件: 图片声音
    • 其他: 可以潜入定制的数据资源。
5.简单描述一下客户端的缓存机制?
  • 缓存可以分为:
    • 内存数据缓存
    • 数据库缓存
    • 文件缓存
  • 每次想获取数据的时候
    1. 检查内存中有无缓存
    2. 检查本地有无缓存(数据库\文件)
    3. 发起请求
    4. 将服务器返回的数据进行缓存(内存数据库文件)

1.按钮或者 View 在进行 frame 动画的时候,为什么点击 frame 动画的重点位置才能触发事件?如何在动画过程中响应事件?

答:
① 动画开始后有两个图层:
(修改原因:不是动画开始后,而是 view 出现了的时候。只要 view 出现在试图上,他都具有这两个图层。)
iOS 中对于 UIView 具有一个 CALayer 类的属性 layer 我们姑且叫他 rootLayer。而每一个 CALayer 都有两个属性:
一个 view.layer.presentationLayer 用来显示动画(属性随动画过程慢慢改变)
(修改原因:感觉显示动画这个用法不大准确,我语文也不好😂)
一个resentationLayer 指呈现图层,是模型图层的复制,他的属性值代表了在任何指定时刻当前的显示。所以在 UIView 没有出现的时候这个 view 的属性值为 nil, 出现之后无论 view 是否动画,这个值的内存地址每次打印都不一样。

一个 view.layer 用来处理用户交互(动画一开始属性值立即改变到动画结束时的状态和位置)
(修改原因: CALayer 是不处理交互的,这一点可以从UIView有userInteractionEnabled这个属性,而CALayer没有可以看出,并且看 iOS-CoreAnimation这本书,这样使用 modelLayer 来描述更 MVC 一些)
一个modelLayer 指模型层,在 view 显示出来的时候view.layer = view.layer.presentationLayer.modelLayer = view.layer.view.layer.modelLayer。 三者内存地址是一样的,保存实际的属性值。

最后总结:当一个图层的属性改变的时候,我们设置的属性并没有直接调用图层的外观,只是定义了图层动画结束之后将要变化的外观。具体关系见下图:


from《iOS-CoreAnimation》

② 要在移动动画的过程中处理 触摸可以重写 UIView的hitTest 方法
(总的来说,通过view.layer.presentationLayer的相关属性,就可以实现响应需求了,通过touchbegan判断用户点击的位置是否在view.layer.presentationLayer.frame之内,是《iOS—CoreAnimation》中另外一个实现方式)

1
2
3
4
5
6
7
8
9
10
11
12
- (id)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSArray *subViews = self.subviews;
for (UIView *subView in subViews) {
if([subView isKindOfClass:[FoamImageView class]]){ //是要找的图片
CALayer *layer = subView.layer.presentationLayer; //图片的显示层
if(CGRectContainsPoint(layer.frame, point)){ //触摸点在显示层中,返回当前图片
return subView;
}
}
}
return [super hitTest:point withEvent:event];
}
1
2
3
4
5
6
7
8
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGPoint point = [[touches anyObject] locationInView:self.view];
if (CGRectContainsPoint(button_.layer.presentationLayer.frame, point)) {
NSLog(@"youxiangying");
[self buttonEvent:button_];
}
}

参考资料
资料1

Objective-C 之 @property和@synthesize

回炉篇之(一)– @property和@synthesize

iOS开发:Objective-C精确的货币计算

‘NSDecimalNumber–十进制数’使用方法(带例子)

objective c-OC中有方法重载吗?具体是怎么样?

http://www.cnblogs.com/LiLihongqiang/p/5645907.html
http://www.infocool.net/kb/IOS/201610/200104.html
http://www.cocoachina.com/ios/20160113/14896.html
http://www.jianshu.com/p/2e074db792ba

think python

www.voidcn.com
iOS-CoreAnimation


写在后面的话

这个系列所有的文章都是在一个QQ群里面跟小伙伴们讨论出来的,每个 topic 都已经放在了 GitHub 的一个仓库中 这是地址。 所有内容的更新的将在这个仓库中进行,在博客中将不再进行更新。有任何问题,都可以在这个仓库中给我们提 issue 或者 pull request.

CepheusSun wechat
订阅我的公众号,每次更新我都不一定会告诉你!
坚持原创技术分享,您的支持将鼓励我继续创作!
0%