猿辅导面试题

猿辅导面试题

猿辅导面试题

1.@[]; 加入 nil 会闪退吗

会闪退,报错:

1
-[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[4]

2.array[i] 会不会走 objectAtIndex

会走。

array[i] 如果数组越界,会崩溃报错:

1
[__NSArrayI objectAtIndexedSubscript:]: index 6 beyond bounds [0 .. 4]'

3.信号量 3 个网络接口返回执行 4

有些时候,我们需要阻塞发送请求的线程,比如在多个请求回调后统一操作的需求,而这些请求之间并没有顺序关系,且这些接口都会另开线程进行网络请求的。一般地,这种多线程完成后进行统一操作的需求都会使用队列组(dispatch_group_t)来完成,但是由于是异步请求,没等其异步回调之后,请求的线程就结束了,为此,就需要使用信号量来阻塞住发请求的线程。实现代码如下:

1
2
3
4
5
6
7
8
dispatch_async(queue, 0), ^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[网络请求:^{
//请求回调
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
});

这样,请求的线程就可以等到回调结束后再结束了,再配合队列组就能完成上述的需求。这种技巧可用于以下场景:

  • 多个请求结束后统一操作
  • 多个请求顺序执行

4.hook delelgate 方法

参考链接:iOS hook delegate

5.交换方法传入参数 为什么需要先 addMethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+ (void)exchangeInstanceMethodWithSelfClass:(Class)selfClass
originalSelector:(SEL)originalSelector
swizzledSelector:(SEL)swizzledSelector {

Method originalMethod = class_getInstanceMethod(selfClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(selfClass, swizzledSelector);
BOOL didAddMethod = class_addMethod(selfClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(selfClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}

防止影响父类的方法。

  • init 方法并不是 Person 类本身的实例(对象)方法,而是父类 NSObject 的方法。由于 Person 本身没有该方法,所以 class_getInstanceMethod 获取到的方法是通过 Personsuperclass 指针从 NSObject 类中获取到了 init 这个方法。

  • method_exchangeImplementations 操作将 NSObjectinit 方法的实现与 Person 类的 yxc_init 方法的实现进行互换了,这时候调用 init 方法实际上是调用了 yxc_init 方法。

  • 创建一个 Person 对象时,调用 init 方法,运行时会去查找 yxc_init 的实现,因为 yxc_init 方法是 Person 自身的方法,所以查找到了直接调用。(消息发送机制)

  • 而创建一个 NSObject 对象时,调用 init 方法,运行时去查找 yxc_init 方法的时候,NSObject 是没有这个方法,这个方法存在于 Person 类中,所以查找完毕,还是找不到这个方法,就抛异常了。

  • 正确的 hook 做法是,先将 init 方法添加到 Person 类中,如果这个类当前有这个方法(而不是父类),则不添加,直接 exchange,否则添加了 init 方法,然后再将 yxc_init 方法的实现设置成 init 方法的实现。

参考链接:iOS Runtime 黑魔法方法交换(Method swizzling)

6.关联对象有几种策略,如何实现 weak

在关联对象释放的时候,调用objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_ASSIGN)这样就把宿主对象的该 key 的关联对象清除了,外部读这个 key 的关联对象就是 nil

1
2
3
4
5
6
7
8
9
10
11
extern void objc_setWeakAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value);

void objc_setWeakAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value) {
if (value) {
//__weak typeof(object) weakObj = object;
[value hc_doSthWhenDeallocWithBlock:^(NSObject *__unsafe_unretained _Nonnull target) {
objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_ASSIGN); // clear association
}];
}
objc_setAssociatedObject(object, key, value, OBJC_ASSOCIATION_ASSIGN); // call system imp
}

参考链接:OC-AssociatedObject原理及weak关联对象的实现

7.响应者链 button 超出父视图一半能否响应点击事件

超出父视图的部分不能响应,在父视图内的部分可以响应。

如何扩大按钮的点击范围?

可以重写 button 的 pointInside: 方法

1
2
3
4
5
6
7
8
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// 当前btn的大小
CGRect btnBounds = self.bounds;
// 扩大按钮的点击范围,改为负值
btnBounds = CGRectInset(btnBounds, -15, -15);
// 若点击的点在新的bounds里,就返回YES
return CGRectContainsPoint(btnBounds, point);
}

8.离屏渲染,为什么会触发

参考链接:知乎-关于iOS离屏渲染的深入研究

简书-iOS离屏渲染产生的原因

9.判断当前任务在哪个 queue 中

dispatch_queue_get_label

参考链接:判断代码在哪个队列中运行

10.SDWebImage 内存缓存怎么实现的

参考链接:iOS-SDWebImage缓存机制

11.json 转 model,Runtime 能否获取到自定义属性的类型

class_copyPropertyList可以获取到属性列表

property_getName可以获取到属性名

property_getAttributes可以获取到成员类型

12.tableView 快速滑动 卡顿发生可能原因

1.提前计算并缓存好高度,因为 heightForRow 最频繁的调用。

- (UITableViewCell)tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath;

2.异步绘制,遇到复杂界面,性能瓶颈时,可能是突破口。

3.滑动时按需加载,这个在大量图片展示,网络加载时,很管用。(SDWebImage 已经实现异步加载)。

4.重用 cells。

5.如果 cell 内显示得内容来自 web,使用异步加载,缓存结果请求。当 cell 中的部分 View 是非常独立的,并且不便于重用的,而且“体积”非常小,在内存可控的前提下,我们完全可以将这些 view 缓存起来。当然也是缓存在模型中。

6.少用或不用透明图层,使用不透明视图。对于不透明的 View,设置 opaque 为 YES,这样在绘制该 View 时,就不需要考虑被 View 覆盖的其他内容(尽量设置 Cell 的 view 为 opaque,避免 GPU 对 Cell 下面的内容也进行绘制)

7.减少 subViews。分析 Cell 结构,尽可能的将 相同内容的抽取到一种样式 Cell 中,前面已经提到了 Cell 的重用机制,这样就能保证 UITbaleView 要显示多少内容,真正创建出的 Cell 可能只比屏幕显示的 Cell 多一点。虽然 Cell 的’体积’可能会大点,但是因为 Cell 的数量不会很多,完全可以接受的

8.少用 addView 给 cell 动态添加 view,可以初始化的时候就添加,然后通过 hide 控制是否显示。

13.括号算法

LeetCode 题目链接:括号生成

14.CocoaPods 缓存存在哪里,pod install 执行

~/Library/Caches/Cocoapods

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×