面试在iOS开发的道路上越走越远iOS面试

孙源面试题试解(更新完毕)

2015-07-19  本文已影响4625人  MD5Ryan

孙源的面试题是通过唐巧的微信公众号看来的, 地址
这里尝试解一下, 肯定会有不少错误, 或者是不会答的, 不会的或者有疑问的会努力查证, 我写在这里, 对自己以示驱策.

PS: 图片是保存下来之后直接上传的, 侵删.

1. 代码风格

Q:

A:

typedef enum
{
    UserSexMan,
    UserSexWoman
}UserSex;

@interface UserModel : NSObject

@property (nonatomic, copy)   NSString *name;
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, assign) UserSex sex;

- (instancetype)initWithUserName:(NSString *)name age:(NSUInteger)age;
- (void)doLogin;

</code>
这里前前后后改了10处

2. property相关

一大波问题正在袭来

  1. 如果不爽@property给你自定义的加_的方式, 可以自己指定
  2. 实现一个协议, 这个协议里面有@property, 但是不会自动synthesize, 所以要自己写@synthesize (propertyName) = _(propertyName)

A:
Son
Father(误)
// 感谢an陌生_爱评论指正:
Son
我觉得这里的原因应该是, Class这个方法是NSObject协议里面的方法, 其实Son和Father都没有实现它, 所以NSObject本身对它的实现是找到实例的isa,然后返回isa里面记录的Class信息, 而无论是super还是self其实都指向的是同一个实例, 因此返回的就是同一个类--即Son. 如果父类和子类都实现Class方法, 分别返回自己的类, 其实就可以得到上面是Son 下面是 Father的输出了

-Q: [※※※※]runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)
// 7.22补充
A:类方法:
class_getMethodImplementation(objc_getMetaClass("A"),@selector(classMethod));
实例方法:
class_getMethodImplementation([A class],@selector(someMethod));
其内部实现应该是找到Class结构体里面的methodLists(类方法和实例方法保存在不同的method_list中, 根据class_getMethodImplementation第一个参数来区分), 通过objc_method结构体来找到对应的实现细节, 更多的细节, runtime里面应该会有更详尽的描述.

-Q: [※※※※]使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?
//7.22补充:
A: 不需要, 会在对象生命周期结束后自动释放; 参考资料

-Q: [※※※※※]objc中的类方法和实例方法有什么本质区别和联系?
A: 类方法不需要示例就可以调用, 实例方法必须要初始化出实例才可以调用
// 7.26补充:
上面的答案只是表象上的区别, 本质上应该可以从class_getMethodImplementation方法的第一个参数窥探一番, 如果是类方法则要传入objc_getMetaClass(@"ClassName"), 实例方法则是objc_getClass(@"ClassName"); 说明这2种方法保存在不同的method_list中.

-Q: [※※※※※]_objc_msgForward函数是做什么的,直接调用它将会发生什么?
A: 在找不到对应方法实现的时候调用, 在没有实现消息转发机制的情况下直接调用应该会crash;
// 7.26补充:
根据我的实验, 先通过class_getMethodImplementation拿到_objc_msgForward的IMP, 再调用, 会出现EXC_BAD_ACCESS错误.

-Q: [※※※※※]runtime如何实现weak变量的自动置nil?
A: //7.26补充:(直接摘抄了公布答案部分, 最近也看了源码, 是这么回事的)
runtime对注册的类会进行布局, 对于weak对象会放入到一张hash表中. 用weak纸箱的对象地址作为key, 当次对象的引用计数器为0的时候会dealloc, 进而在这个weak表中找到此对象地址为键的所有weak对象, 从而设置为nil

-Q: [※※※※※]能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
A: // 7.26补充:(直接摘抄公布答案部分, 准备实验一下)
因为编译后的类已经注册到runtime中, 类和结构体的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定, 同事runtime会调用class_setIvarLayout或class_setWeakIvarLayout来处理strong weak引用. 所以不能向已存在的类中添加实例变量;
运行时创建的类是可以添加实例变量, 调用class_addIvar函数, 但是得在调用objc_allocateClassPair之后, objc_registerClassPair之前, 原因同上.
// 个人想法: 类可以注册, 应该也可以反注册, 之后再用动态创建的办法, 增加实例变量, 当然, 原有的此类变量可能受到影响, 又需要各种检查 整理什么的. 也许是这种做法太不稳定, 而且没有什么必需的场景, 估计也不会去实现.

-Q: [※※※]runloop和线程有什么关系?
A: 一一对应

-Q: [※※※]runloop的mode作用是什么?
A: //7.26补充:
Apple Document这么说的"A run loop mode is a collection of input sources and timers to be monitored and a collection of run loop observers to be notified"(runloop mode是一组需要监听的输入源和定时器以及一组要被通知runloop的观察者)

-Q: [※※※※]以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?
A: //7.26补充:
与runloop的mode有关, NSTimer运行在Default Mode下, 而滑动页面列表会让主线程的runloop设置为TrackingRunLoopMode, 这个时候NSTimer是不能fire的.

-Q: [※※※※※]猜想runloop内部是如何实现的?
A: do
{
// get message and send
}while (signal != quit)

-Q: [※]objc使用什么机制管理对象内存?
A: ARC

-Q: [※※※※]ARC通过什么方式帮助开发者管理内存?
A: 编译器在恰当的地方增加retain release方法

-Q: [※※※※]不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)
A: 出了作用域之后释放

-Q: [※※※※]BAD_ACCESS在什么情况下出现?
A: 访问已经释放对象的成员变量或者发消息

-Q: [※※※※※]苹果是如何实现autoreleasepool的?
A: 让编译器在合适的地方自动加入retain和release方法

-Q: [※※]使用block时什么情况会发生引用循环,如何解决?
A: block被对象持有, block又持有了对象; 使用weak

-Q: [※※]在block内如何修改block外部变量?
A: 在变量前加上__block关键字

-Q: [※※※]使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?
A: 不需要, 因为对象并没有持有这个block

-Q: [※※]GCD的队列(dispatch_queue_t)分哪两种类型?
A: //7.26补充:(直接摘抄了公布答案部分)
并发和串行

-Q: [※※※※]如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
A: //7.26补充:(直接摘抄了公布答案部分)
使用dispatch_group, 然后wait forever等待完成, 或者采用group notify来通知回调

-Q: [※※※※]dispatch_barrier_async的作用是什么?
A: //7.26补充:(直接摘抄了公布答案部分)
在并行队列中, 为了保持某些任务的顺序, 需要等待一些任务完成后才能继续进行, 使用barrier来等待之前的任务完成, 避免数据竞争等问题.
// 那上一题应该也可以用barrier来解决吧?

-Q: [※※※※※]苹果为什么要废弃dispatch_get_current_queue?
A: //7.26补充:
在Effective of Objective-C中的Item46专门讲了这个函数, 主要原因还是因为这个函数并不总是像我们想象的那样执行, 因为Dispatch队列是以层次方式来组织的, 因此, 当前队列不能简单地用一个队列对象来描述(讲白了就是容易冲突); 而且, Queue-specific数据可以很好地解决需要用dispatch_get_current_queue来解决不可重入代码死锁的场景.

-Q: [※※※※※]以下代码运行结果如何?
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
A: // 7.26补充:
根据实验, 只会输出1, 原因应该是UI绘制在主线程, 这边又在主线程同步调用block, 直接造成了主线程被阻塞, sync的里面的block代码又要在主线程运行, 这样就造成了死锁. 将sync修改为async 或者 get_main_queue改为get_global_queue都可以避免这个问题.

-Q: [※※]addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?
A: Observer是响应的对象, KeyPath是字段名, options则是通知类型, context可以把数据传给回调;
在- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context接受回调

-Q: [※※※]如何手动触发一个value的KVO
A: 没有重写@property生成的set方法情况下, 用点语法修改值可以触发. 用setvalue:forKey:也可以触发

-Q: [※※※]若一个类有实例变量NSString *_foo,调用setValue:forKey:时,可以以foo还是_foo作为key?
A: foo

-Q: [※※※※]KVC的keyPath中的集合运算符如何使用?
A: 分别有@sum, @count, @avg, @max, @min, 使用:
[array valueForKeyPath:@"@count"]; // 相当于array.count, 输出一个NSNumber型
[array valueForKeyPath:@"@sum.a"]; // a属性的和
[array valueForKeyPath:@"@avg.b"]; // b属性的平均值
[array valueForKeyPath:@"@max.c"]; // c属性的最大值
[array valueForKeyPath:@"@min.d"]; // d属性的最小值
max和min是调用compare:来确定的, 所以要实现之.

-Q: [※※※※]KVC和KVO的keyPath一定是属性么?
A: // 7.26 修正
以前做过实验, 如果不是属性, 不会回调到observeValueForKayPath:...(误)
以上答案是对KVO KVC了解过于肤浅的结果, 实际上还有很多的用途, 如上一题所示.更多的可以参考KVC Collection Operator

-Q: [※※※※※]如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?
A: //7.26补充:
实现类方法, +(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key, 根据需要返回NO, 然后实现

-Q: [※※※※※]apple用什么方式实现对一个对象的KVO?
A: // 7.26补充:(摘抄自Limboy's HQ)
通过runtime实现的。当你第一次观察某个object时,runtime会创建一个新的继承原先class的subclass。在这个新的class中,它重写了所有被观察的key,然后将object的isa指针指向新创建的class(这个指针告诉Objective-C运行时某个object到底是哪种类型的object)。所以object神奇地变成了新的子类的实例。
这些被重写的方法实现了如何通知观察者们。当改变一个key时,会触发setKey方法,但这个方法被重写了,并且在内部添加了发送通知机制。(当然也可以不走setXXX方法,比如直接修改iVar,但不推荐这么做)。
有意思的是:苹果不希望这个机制暴露在外部。除了setters,这个动态生成的子类同时也重写了-class方法,依旧返回原先的class!如果不仔细看的话,被KVO过的object看起来和原先的object没什么两样。

-Q: [※※]IBOutlet连出来的视图属性为什么可以被设置成weak?
A: 因为xib文件已经强持有了这个视图(猜测, 个人不用xib)

-Q: [※※※※※]IB中User Defined Runtime Attributes如何使用?
A: 不太用IB, 貌似就是不需要写代码就定义一些在IB上没法直接操作的属性, 比如layer的属性之类的, 更多的信息可以看下参考资料

-Q: [※※※]如何调试BAD_ACCESS错误
A: 1. 开启NSZombie 2. 用image lookup --address BAD_ACCESS的地址空间对应的对象

-Q: [※※※]lldb(gdb)常用的调试命令?
A: image, po, p, call, watchpoint, expr

感觉非常多的runtime的问题, 可能是想说明, 对runtime理解不够深入的OC程序员可能不是一个合格的iOS开发者, 好在apple开源了runtime的代码, 目前正在研读, 希望自己对这方面的理解更深入 彻底一些.

其它的非技术性的东西就不贴了, 都是见仁见智的问题, 一般按自己的想法说就好了, 个人感觉对这类问题还是不要牵强地迎合, 毕竟面试是双向的, 公司在选我们, 我们也在选公司, 我们可以从这些问题上面看出面试官的一些喜好, 从而窥探这个公司的文化, 如果实在不符合自己的性格, 个人还是建议不要强求, 人生已经如此艰难, 我们总得让自己过的舒服一点.

上一篇下一篇

猜你喜欢

热点阅读