一面
一、自我介绍
简单介绍一下你自己吧
解析:简单介绍下自己的名字,教育背景,现在的工作,做过的项目
二、自我介绍衍生的口头问题
讲讲下你在你项目中做过的优化或者技术难点
解析:介绍了自己封装的一个集picker,文本域的灵活展开的表视图。这个视图的数据源是json,怎么转成模型数组的?这个cell有哪些类型?展示的怎么区分这些cell?这里面有用过复用机制吗?这些cell有实现过多重继承吗?
题外话:这种问题最好各人自己找问题讲讲,不多,提前准备一个你项目中非常擅长并熟悉的点,即可。
1. 你平时怎么解决网络请求的依赖关系:当一个接口的请求需要依赖于另一个网络请求的结果
解析:
- 办法1:操作:NSOperation 操作依赖和优先级(不适用,异步网络请求并不是立刻返回,无法保证回调时再开启下一个网络请求)
[operationB addDependency:operationA]; // 操作B依赖于操作
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并图片… …
});
- 办法4:线程同步 --阻塞任务(dispatch_barrier):
/* 创建并发队列 */
dispatch_queue_t concurrentQueue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
/* 添加两个并发操作A和B,即A和B会并发执行 */
dispatch_async(concurrentQueue, ^(){
NSLog(@"OperationA");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"OperationB");
});
/* 添加barrier障碍操作,会等待前面的并发操作结束,并暂时阻塞后面的并发操作直到其完成 */
dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"OperationBarrier!");
});
/* 继续添加并发操作C和D,要等待barrier障碍操作结束才能开始 */
dispatch_async(concurrentQueue, ^(){
NSLog(@"OperationC");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"OperationD");
});
- 办法5:线程同步 – 信号量机制(dispatch_semaphore):
/* 创建一个信号量 */
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
/* 任务1 */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* 耗时任务1 */
NSLog(@"任务1开始");
[NSThread sleepForTimeInterval:3];
NSLog(@"任务1结束");
/* 任务1结束,发送信号告诉任务2可以开始了 */
dispatch_semaphore_signal(semaphore);
});
/* 任务2 */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* 等待任务1结束获得信号量, 无限等待 */
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
/* 如果获得信号量则开始任务2 */
NSLog(@"任务2开始");
[NSThread sleepForTimeInterval:3];
NSLog(@"任务2结束");
});
[NSThread sleepForTimeInterval:10];
2. 关于RAC你有怎样运用到解决不同API依赖关系
使用场景是当信号A执行完才会执行信号B,和请求的依赖很类似,例如请求A请求完毕才执行请求B,我们需要注意信号A必须要执行发送完成信号,否则信号B无法执行
//这相当于网络请求中的依赖,必须先执行完信号A才会执行信号B
//经常用作一个请求执行完毕后,才会执行另一个请求
//注意信号A必须要执行发送完成信号,否则信号B无法执行
RACSignal * concatSignal = [self.signalA concat:self.signalB];
//这里我们是对这个拼接信号进行订阅
[concatSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
3. 编译链接你有了解多少
解析:这个涉及到简单知识,可参考http://www.360doc.com/content/17/0111/22/32626470_621879084.shtml
4. 简单介绍下KVO的用法
解析:首先,简单介绍下KVO的用法,先添加观察者,然后怎样实现监听的代理。关于原理,可以利用runtime的知识进行实现KVO的原理,笔者曾经实现KVO的block和delegate两种形式
简单概述下 KVO 的实现原理:
当你观察一个对象时,一个新的类会动态被创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。自然,重写的 setter 方法会负责在调用原 setter方法之前和之后,通知所有观察对象值的更改。最后把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。
原来,这个中间类,继承自原本的那个类。不仅如此,Apple 还重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类。更具体的信息,去跑一下 Mike Ash 的那篇文章里的代码就能明白,这里就不再重复。
5. 介绍下Block的理解
本质是个匿名函数,可以用clang编译一下block块,可以看到它的内部。再讲讲平时怎样避免block里面可能产生的引用循环。再讲讲block怎样捕获变量的,最后讲讲block的有哪些类型,有什么区别(栈block,堆block,全局block)
6. 平时怎么解决多线程安全的问题
解析1:NSLock(),还可以围绕YYModel的线程安全方案mutex讲讲。
解析2:区分比较各自的优劣:atomic,sychronized,dispatch_semaphore_signal,重写setter方法里面串行队列
(1) nonatomic atomic:使用atomic多线程原子性控制,atomic的原理给setter加上锁,getter不会加锁。OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic) nonatomic:非原子属性,不会为setter方法加锁
(2) 使用GCD实现atomic操作:给某字段的setter和getter方法加上同步队列:
- (void)setCount:(NSInteger)newcount{
dispatch_sync(_synQueue, ^{ count = newcount; });
}
- (NSInteger)count
{
__block NSInteger localCount;
dispatch_sync(_synQueue, ^{
localCount = count;
});
return localCount;
}
7. static与extern的区别
解析:static修饰的是静态变量,保存在全局静态区。extern主要是跨文件访问用的。
8. Block里面怎么避免变量被回收,不是改变外部变量的问题
解析:主要避免栈变量的回收,栈变量的生命周期和堆block的生命不一致可能导致调用crash。
9. Masory的Block体里面怎样避免的循环引用
解析:这个block体里面放心使用self等等,不用weakSelf。并不是所有block里面要用weakSelf,关键看有没有形成引用循环。Masory的block虽然应用了self,但self并没拥有block。所以,不用。
二面
1. 编程题:RLE算法,编写一个函数,实现统计字符次数的功能:例如输入为aaabbccc,输出为a3b2c3。不限语言。
解析:比较简单,可以参考这 http://blog.51cto.com/lanchaohuan/1563103
2. 编程题:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的.
解析:思路,递归,从根节点开始,判断左右子节点是否对称,若对称,递归,若不对称,则返回NO。
3. 问答题:你认为自动布局怎么实现的
item1.attribute = multiplier ? item2.attribute + constant
基于这个原理的代码API是:
//redView(红色)top 距离self.view的top
NSLayoutConstraint *redViewTopToSuperViewTop = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:30];
这个具体代码对应的约束是: view_1的顶部(y)= self.view的顶部(y)*1 + 30。
4. 问答题:runtime的知识你有哪些实际应用?(根据简历问的)
三面
编程题:实现以下功能
- 编写一个自定义类:Person,父类为NSObject
- 解析:头文件这样写
@interface Person:NSObject
- 该类有两个属性,外部只读的属性
name ,还有一个属性age
- 解析:
name 的修饰符nonatomic ,strong ,readonly 。age 的修饰符nonatomic ,copy 。
- 为该类编写一个初始化方法
initWithName:(NSString *)nameStr ,并依据该方法参数初始化name 属性。
- 如果两个Person类的name相等,则认为两个Person相等
- 解析:重写
isEqual ,这里面涉及到了哈希函数在iOS中的应用。
由编程题衍生的口头题目
1
题目: 怎样实现外部只读的属性,让它不被外部篡改
解析:
-
头文件用readonly修饰并声明该属性。正常情况下,属性默认是readwrite,可读写,如果我们设置了只读属性,就表明不能使用setter方法。在.m文件中不能使用self.ivar = @"aa"; 只能使用实例变量_ivar = @"aa"; ,而外界想要修改只读属性的值,需要用到kvc赋值[object setValue:@"mm" forKey:@"ivar"]; 。 -
实现文件里面声明私有属性,并在头文件在protocol里面规定该属性就可以了,外部通过protocol获取,这样还可以达到隐藏成员的效果。
2
题目: nonatomic是非原子操作符,为什么要这样,atomic为什么不行?有人说能atomic耗内存,你觉得呢?保读写安全吗,能保证线程安全吗?有的人说atomic并不能保证线程安全,你觉得他们的出发点是什么,你认同这个说法吗?
如果该对象无需考虑多线程的情况,请加入这个属性修饰,这样会让编译器少生成一些互斥加锁代码,可以提高效率。
而atomic这个属性是为了保证程序在多线程情况下,编译器会自动生成一些互斥加锁代码,避免该变量的读写不同步问题。
atomic 和 nonatomic 的区别在于,系统自动生成的 getter/setter 方法不一样。如果你自己写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示作用,写不写都一样。
苹果的官方文档有解释,下面我们举例子解释一下背后的原理。
//@property(nonatomic, retain) UITextField *userName;
//系统生成的代码如下:
- (UITextField *) userName {
return userName;
}
- (void) setUserName:(UITextField *)userName_ {
[userName_ retain];
[userName release];
userName = userName_;
}
//@property(retain) UITextField *userName;
//系统生成的代码如下:
- (UITextField *) userName {
UITextField *retval = nil;
@synchronized(self) {
retval = [[userName retain] autorelease];
}
return retval;
}
- (void) setUserName:(UITextField *)userName_ {
@synchronized(self) {
[userName release];
userName = [userName_ retain];
}
}
简单来说,就是 atomic 会加一个锁来保障多线程的读写安全,并且引用计数会 +1,来向调用者保证这个对象会一直存在。假如不这样做,如有另一个线程调 setter,可能会出现线程竞态,导致引用计数降到0,原来那个对象就释放掉了。
atomic修饰的属性只能说是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。
因为atomic修饰的属性靠编译器自动生成的get和set方法实现原子操作,如果重写了任意一个,atomic关键字的特性将失效
3
题目: 你在初始化的方法中为什么将参数赋给_name,为什么这样写就能访问到属性声明的示例变量?
- xcode4 之后,编辑器添加了自动同步补全功能,只需要在 h 文件中定义 property,在编译期m文件会自动补全出
@synthesize name = _name 的代码,不再需要手写,避免了“体力代码”的手动编码
4
题目: 初始化方法中的_name是在什么时候生成的?分配内存的时候吗?还是初始化的时候?
-
成员变量存储在堆中(当前对象对应的堆得存储空间中) ,不会被系统自动释放,只能有程序员手动释放。 -
编译的时候自动的为name属性生成一个实例变量_name -
如果m中什么都不写,xcode会默认在编译期为 market 属性,补全成 @synthesize market = _market,实例变量名为 _market; -
如果m中指定了 @synthesize market,xcode会认为你手动指定了实例变量名为 market ,编译期补全成:@synthesize market = market,实例变量名为 market。
5
题目: 作为return的self是在上面时候生成的?
-
是在alloc时候分配内存,在init初始化的。 -
一种典型带成员变量初始化参数的代码为:
- (instancetype)initWithDistance:(float)distance maskAlpha:(float)alpha scaleY:(float)scaleY direction:(CWDrawerTransitionDirection)direction backImage:(UIImage *)backImage {
if (self = [super init]) {
_distance = distance;
_maskAlpha = alpha;
_direction = direction;
_backImage = backImage;
_scaleY = scaleY;
}
return self;
}
6
题目: 为什么用copy,哪些情况下用copy,为什么用copy?
- 可变的类,例如NSArray、NSDictionary、NSString最好用copy来修饰,它们都有对应的Mutable类型。
- copy修饰属性的本质是为了专门设置属性的setter方法,例如,
setName: 传进一个nameStr参数,那么有了copy修饰词后,传给对应的成员变量_name的其实是[nameStr copy]; 。 - 为什么要这样?如果不用copy会有什么问题?例如,
strong 修饰的NSString类型的name属性,传一个NSMutableString:
NSMutableString *mutableString = [NSMutableString stringWithFormat:@"111"];
self.myString = mutableString;
在strong 修饰下,把可变字符串mutableString赋值给myString后,改变mutableString的值导致了myString 值的改变。而copy 修饰下,却不会有这种变化。
在strong 修饰下,可变字符串赋值给myString后,两个对象都指向了相同的地址。而copy 修饰下,myString和mutableString指向了不同地址。这也是为什么strong修饰下,修改mutableString引起myString变化,而copy修饰下则不会。
- 总之,当修饰可变类型的属性时,如NSMutableArray、NSMutableDictionary、NSMutableString,用strong。当修饰不可变类型的属性时,如NSArray、NSDictionary、NSString,用copy。
7
题目: 分类中添加实例变量和属性分别会发生什么,编译时就报错吗,还是什么时候会发生问题?为什么
另外聊到的实际开发问题
-
你平时有做过优化内存的哪些工作?怎样避免内存消耗的大户? -
你怎样实现线程安全的?这些线程安全的办法和atomic有什么不一样?atomic的实现机制是怎样,如果不加atomic会怎么样呢?
同时我也整理了一些面试题,有需要的朋友可以加QQ群:1012951431 获取
|