您的位置:首页 > 职场人生

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

2015-08-05 15:15 211 查看
孙源的面试题是通过唐巧的微信公众号看来的,
地址
这里尝试解一下, 肯定会有不少错误, 或者是不会答的, 不会的或者有疑问的会努力查证, 我写在这里, 对自己以示驱策.
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;

这里前前后后改了10处
2. property相关

Q:@property 后面可以有哪些修饰符?

A: 按类别来说有访问原子性, 读写权限和内存管理方式和方法指定;

原子性分为nonatomic和atomic,

读写分为readonly和readwrite,

内存管理方式分为assign, weak, strong, copy, unsafe_unretained

方法指定为:getter=XXX, setter=XXX
Q:什么情况使用 weak 关键字,相比 assign 有什么不同?

A: 如果指向某个对象, weak会在对象释放的时候置为nil, assign则不会
Q:怎么用 copy 关键字?

A: (这个问法好奇怪)在@property中定义属性内存管理方式时用copy
Q:这个写法会出什么问题: @property (copy) NSMutableArray *array;

A:没有定义为nonatomic, 默认是atomic, 在访问array的时候性能极度下降,

// 感谢an陌生_爱评论补充:

外面不管传值为NSMutableArray和NSArray对象,array都是NSArray,编译器编译时还是会认为是NSMutableArray,调用addObject就会崩溃
Q:如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?

A: 实现NSCopy协议; 在copy方法中新建一个自己的类的对象, 然后给全部的成员变量赋值为自己对应的变量值
Q:@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的

A: @property的本质应该是告诉编译器, 按照给定的管理方式来生成属性的getter和setter方法: getter是直接生成属性同名的方法, setter是加上set前缀, 大写属性第一个字母并需要传入属性类型的变量的方法

关于ivar, 应该是直接在类的结构体中, 加入到ivars的list中就好了
Q:@protocol 和 category 中如何使用 @property

A:据我所知, @protocol使用@property与在类中并无区别, 而一般不推荐在category增加类的属性
Q:runtime 如何实现 weak 属性
A:用objc_setAssociatedObject(...), 选择对应的Enum即可(误)
// 感谢an陌生_爱评论指正:
首先会调用objc_initWeak(&obj1, obj);
在一系列检查之后会调用objc_storeWeak(id location, id value);

storeweak操作会调用unregister移除旧对象在weak表中的指向信息(如有)

最后location = newObj; 然后register新对象的weak表中的指向信息

一大波问题正在袭来

Q:[※]@property中有哪些属性关键字?

A: 同上
Q:[※]weak属性需要在dealloc中置nil么?

A: 不需要, weak并不引用指向对象, 何况就算引用也不需要. dealloc中在ARC情况下一般用作解除KVO和Notification的监听
Q:[※※]@synthesize和@dynamic分别有什么作用?

A: @synthesize原来用作生成getter和setter方法, 目前基本只用作指定@property声明属性在类内部访问的名字

@dynamic是告诉编译器不需要由它来声明getter和setter方法, 要自己实现, 常见于coredata
Q:[※※※]ARC下,不显示指定任何属性关键字时,默认的关键字都有哪些?

A: 我所知的有atomic, readwrite, 至于内存管理方式, 不确定
Q:[※※※]用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

A: 防止这些属性在不知情的情况下被修改, 这种情况比较难DEBUG, 使用strong会暴露这个问题
Q:[※※※]@synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?

// 7.22补充:

A: 如果没有@synthesize的话, 访问property只能用self.property;

用@synthesize可以用两种用法, 一种是@synthesize property. 这样的话可以直接用类似property = xxx;来读写, 可以省略self.

另一种是, @synthesize property = newName; 可以用newName = XXX;来读写;

无论是哪种方式, 在DEBUG的时候是可以看到多了一个V型的变量(与实例变量一样);

至于第二问, 如果有一个_foo的实例变量, 会不会合成是看情况的, 如果@synthesize之后名字与实例变量一样, 就不会合成, 否则就会. 不合成的情况下, 实例变量的值与property是一起修改的.
Q:[※※※※※]在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?

A: 指定类内部访问变量的方式

// 7.31更新:
如果不爽@property给你自定义的加_的方式, 可以自己指定
实现一个协议, 这个协议里面有@property, 但是不会自动synthesize, 所以要自己写@synthesize (propertyName) = _(propertyName)
Q:[※※]objc中向一个nil对象发送消息将会发生什么?

A: 直接返回nil, 因为发送消息底层是调用objc_msgSend(), 如果对象为空, 会直接返回nil
Q:[※※※]objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?

A: 发送消息底层由objc_msgSend()实现.
Q:[※※※]什么时候会报unrecognized selector的异常?

A: 头文件或者delegate中声明了某个方法却没有被实现

// 感谢an陌生_爱评论指正:

头文件或者delegate中声明了某个方法却没有被实现 ,这个也有点问题,发送消息,显示查找方法数组链,找不到会返回_objc_msgForward函数指针,这里面做了一系列什么动态方法解析,方法重定向,消息转发,最后才会抛这个异常
Q:[※※※※]一个objc对象如何进行内存布局?(考虑有父类的情况)

A: 最上面是父类的内存布局, 下面是自己的内存部分, 都包含在Class结构体中,包括isa指针, 类名, 大小, 成员变量list, 方法list, 协议list, 父类Class结构体,
Q:[※※※※]一个objc对象的isa的指针指向什么?有什么作用?

A: 指向这个类的类型本身, 保存对象的类型信息
Q:[※※※※]下面的代码输出什么?

@implementation Son : Father
-(id)init
{
self = [super init];
if (self)
{
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end

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, 通过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, 然后实现

(void)willChangeValueForKey:(NSString )key

-(void)didChangeValueForKey:(NSString )key
在setter中 手动调用这2个函数即可

-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的代码, 目前正在研读, 希望自己对这方面的理解更深入 彻底一些.
其它的非技术性的东西就不贴了, 都是见仁见智的问题, 一般按自己的想法说就好了, 个人感觉对这类问题还是不要牵强地迎合, 毕竟面试是双向的, 公司在选我们, 我们也在选公司, 我们可以从这些问题上面看出面试官的一些喜好, 从而窥探这个公司的文化, 如果实在不符合自己的性格, 个人还是建议不要强求, 人生已经如此艰难, 我们总得让自己过的舒服一点.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: