iOS面试一些大公司的总结

《iOS面试一些大公司的总结》 无图无封面

写在前面

好久没有在简书写东西了,之前还信誓旦旦承诺的一周一篇,最近几个月一直都很忙,自从9月末从上家公司离职后,终于得了些空放松放松,10月1总会过去的,所以玩够了的我,是时候开始正经找工作喽~
因为目标比较坚定,就是要找一个大公司,所以投的简历也都很有目标性,下面我就总结一下这一周多对于一些大公司的面试情况吧~

自我介绍

程序员的自我介绍是我认为最纠结的了,因为我平时比较明骚,可到了正式场合就语无伦次了,所以这一块我一般都只是简单汇报一下我的背景信息、生辰八字啥的就得了…总之,我遇到的大公司都对这个一笔带过,并没有网上说的要多精辟才行。

简历

在聊面试经历之前,先说说简历吧,我觉得一个人的简历写的怎么样直接影响到了面试的机会,毕竟简历这种东西第一个看的是不懂技术的HR大小姐。
一般iOS简历格式都差不多长一个样:
教育背景
工作经验
专业技能
项目经验
自我评价
个人主页
其中的专业技能和项目经验用到的技术要点切记~~~
一定不要写的太浮夸 !(自己不会的一定不要往上写!)
因为我发现,一般技术比较牛的面试官都会比较羞涩,他不一定会喜欢多看你,而是一个劲的盯着手里的那张简历看个不停,如果再遇到极度内敛性的技术宅,连自我介绍都可以省了,尴尬个半天才问一些你简历上面写到的东西。
但是一些自己擅长或者理解的技术点一定要写的高端简洁一些!比如:
二次封装AFN (无网络走数据缓存)
AFN的封装估计这个谁都写了,不过在后面加一个点睛之笔,对于无网络时的缓存机制,这个可就不一定都有了,面试官会一下子就被吸引住,接着问你怎么个缓存机制,这个时候暗自窃喜去吧,已经被你领上道了,早都准备好在这等你问呢~!

基础题

一般大公司面试都不是一次性就能决定去留的,一般都要经历三面甚至更多,所以一面的面试官如果没让你做题的话,就会先问一些基础题来迅速过滤你是否值得下一步面试,因为这些基础题面试官是一定会的(人家可以扯脖子跟你喊谁的对!),所以这些基础题务必要准备的充分,可能直接影响到你的去留问题!!接下来是一些遇到的常问的问题:
1、iOS属性默认都有什么关键字?iOS9以后增加了什么关键字?
2、strong和copy的用法,strong是深拷贝吗?对于字符串使用strong会有什么问题?对于可变数组使用copy这样会有什么问题?
3、@synthesize和@dynamic是什么?用法?
4、代理是什么?协议/接口 模式应用场景?
5、block可以修改外部变量吗?(为什么直接就可以修改全局变量?使用__block才可以修改局部变量?)
6、block分几种类型?内存区域在什么位置?
7、实现响应链传递的两个方法?(hitTest和pointInside)在哪些地方使用过?
这些基础题我就不写答案了,总之这些基础题算是面试官对你的印象分吧,对于这些题大家一定要查一查,给它背下来,最好能理解下来,如果你能连续答对好几道,一般面试官就直奔更深层的了,不确定对不对也没关系,大胆的说出来,之后看面试官表情就知道对错了,如果错了,就不要纠结了赶紧说可能忘记了不过看一下API就能捡起来,迅速引导进入下一题,所以基础这部分不要犹豫,会就会,不会就赶紧说可能忘记了,不要在那一个劲纠结。

底层

之后就是中级一点的题了,说一千道一万,大公司和小公司面试就是不一样,小公司会比较在乎你是否能胜任独立开发啊,对于公司的app是否有困难啊,有几年工作经验啊,常用到哪些第三方库啊之类的(真不是想黑小公司的意思)
而大公司的技术面试,还真的挺中规中矩,他们会比较关注你的一些对于一些常用的东西底层的理解,接下来我会列举几个被问到的底层问题:
1、KVO的底层实现原理,能否手动实现一个KVO,怎么处理?
2、block底层实际上是什么?block有几种类型?block使用时的注意事项
3、OC对象本质是什么?
4、Runtime是什么?用到过runtime吗?
5、SDWebImage底层实现?还有它的缓存机制?
6、AFNetWorking在成功回调后是处于什么线程之中?为什么会这样?
这类的题,面试官可能会采用一种模拟场景的形式去问你,这样你会感觉不是那么抽象,举个遇到过的例子:
在多人合作开发环境下,可能会有重复添加KVO的时候,或者重复移除KVO的时候,重复添加或删除KVO会怎么样?采取什么方案去解决?

  • 这个问题其实就是看你解决问题的能力及思路,我觉得有两种方法都可以实现,一种就是手动实现一个KVO,KVO本质是苹果偷偷添加了一个派生类,将指针指向派生的子类,去监听子类的set方法从而实现监听的效果,手动实现一个KVO,并修改其添加和移除的方法,使用BOOL值作为属性在方法内部进行判断当前这个对象是否添加过或删除过即可;第二种方法,就是写一个类别,利用runtime去交换kvo的添加或移除方法到自定义的添加或移除方法,修改内部方法实现以达到该效果。

设计思想

一般面到这个时候,如果前两部分都还可以的话,这部分就是给你订价的时候了,面试官会调几个业务需求让你设计一种方案或者设计一个控件怎么封装,其本质还是考察你的设计思想。

  • 在网易面试,被问到的问题:

如果让你去设计SDWebImage的缓存机制,怎么去设计?

  • 我的回答:
    SDWebImage的缓存分为两种,一种是内存缓存(MemoryCache),另一种是硬盘缓存(DiskCache),我会自定义一个单例缓存类,其中会有对应的两种缓存作为属性,另外还会暴露一些自定义属性让用户可以去修改缓存的策略,例如最大缓存字节数,自动进行清理缓存还是不自动去清理等,还会暴露一些方法比如通过key去查找该缓存文件,当前key是否存在于沙盒或者内存中等。

  • 在另一家较大公司(名字不说了)被问到的问题:

如果一个自定义的Cell可能被用在多处,每处后台返回的数据源模型(json原始数据)字段都是不同的,如果让你去设计一种Model去兼容所有的业务需求,即不管原始数据什么样都能一样进行解析赋给Cell,你怎么去设计?

  • 我的回答:
    这个问题我觉得有很多种解决方法,我当时列举了两种,
    • 第一种是采用一个万能Model,即设计一个只针对于View控件的Model,不去管数据源的模型。可以这么理解,这个Model中的所有字段都是针对于View的,而不是原始数据源的,这样当你从AFN请求下来数据后需要再进行二次转换,即将原始数据转换成Model里的字段,这样就不管你是什么原始数据最终都将转换成只针对于View的Model里的字段,即万能字段(这个想法说出之后,不知道为什么那个面试官楞了一下,可能没理解还是我表达的不够清楚,他最后想了想说这个也可以还有没有更好的方法?我心中默念:mmp,难道只有你心里那个答案才是正确的么?)

    • 第二种解决方法是采用 协议/接口 模式,即在Cell中去创建一个代理人,代理人遵守Cell数据的接口,这个协议的所有方法即是赋给Cell数据的方法,当然也只考虑当前这个Cell,不考虑原始数据,不知道这么说能理解么?写段代码:

@protocol CustomCellGetModelProtocol <NSObject>

- (NSString *)getNameFromModel;

- (NSString *)getIconUrlFromModel;

- (NSString *)getGenderFromModel;

@end

@interface CustomTableViewCell : UITableViewCell

@property (nonatomic, strong) UILabel *nameLabel;

@property (nonatomic, strong) UIImageView *icon;

@property (nonatomic, strong) UILabel *genderLabel;

@property (nonatomic, weak) id<CustomCellGetModelProtocol> delegate;

- (void)reloadData;

@end

如上所示,这个CustomTableViewCell本身有一个代理对象遵从CustomCellGetModelProtocol协议去从原始数据中获取该Cell所需要的数据,在内部通过reloadData去赋值(有人会问,为什么需要一个reloadData呢?)是这样的,一般我们数据都是异步在网络请求后成功回调才能得到,所以这个过程不可预知不知什么时候才能请求下来数据,因此控制器需要在网络成功回调拿到数据后去刷新这个Cell,这是reloadData的用处之一,另一个作用就是如果业务需求是可能在同一个控制器中Cell变化数据很多次,那么这个时候可以根据reloadData方法去实现更新,下面放出内部实现:

- (void)reloadData {
    if (!self.delegate) return;
    if ([self.delegate respondsToSelector:@selector(getNameFromModel)]) {
        self.nameLabel.text = [self.delegate getNameFromModel];
    }
    if ([self.delegate respondsToSelector:@selector(getIconUrlFromModel)]) {
        [self.icon sd_setImageWithURL:[self.delegate getIconUrlFromModel]];
    }
    if ([self.delegate respondsToSelector:@selector(getGenderFromModel)]) {
        self.genderLabel.text = [self.delegate getGenderFromModel];
    }
}

数据结构和算法

当然如果你面的公司有需要或者是关于质量分析岗位的iOS(当然也有例外,前几天去了一个小公司面试iOS,上来给了一份题,题上一共9道全是算法题。。。我直接转身就走了),就会被问到数据结构和算法。
我反正这个是个大短板,因为我不是计算机出身,基础不是那么好,对这块只能后天去补,anyway,BAT公司基本都会问的,所以我们还是预防着好一些(网易是个例外,竟然没被问到好开森,而且面的也不错,期待网易的好消息),下面放传送门:
了解数据结构和算法
所有算法

前几天在百度的面试就被面试官无情的问到了:

  • 问:“算法你了解吗?能列举几个常用的吗?”
  • 答:“(常用?我尼玛开发多少app了就特么没用到过你跟我说常用?)嗯。。。了解过,比如什么冒泡排序、插入排序、希尔排序、快排!对,快速排序!”(因为我就会个快排,希望他问我快排)
  • 问:“快速排序的原理是什么?你可以用笔去表述”
  • 答:“(哈哈哈,上钩了吧,老子就特么会一个快速排序)快排就是每次都通过比较前后大小先将一个中间量放置在它该有的位置,其余所有比它小的放在它左侧,同理大的放右侧,之后再进行递归左侧和右侧同样采取这种方法直到所有数据都排序完成”
  • 问:“我们都知道快排平均次数是O(NlogN),那你知道快排的最坏情况的次数么?什么是最坏的情况?”
  • 答:“(哈哈,我尼玛还真就不知道平均次数是多少,倒是记住了最坏的次数因为好记)知道,是O(N2二次方),最坏的情况可能是。。。(一通瞎说,感觉要黑)”
  • 问:“我们都知道每次排序一般都是从第一个数去比较后一个,如果恰好这个数是最大的时候会一直比较到头,这样的情况出现会比较慢,有没有什么办法优化一下速度”
  • 答:“(。。。快排还需要优化速度么?真是日了X)这个。。额。。(又是一通烂说)”

最后面试官终于放弃了对于快排的询问,我刚放松了一下,他竟然又问了一个Google经典的面试题,虽然我当时没听过:

有一个100层的楼,你手里现在有两个一模一样的鸡蛋,它们有可能都在一楼就摔碎,也可能从一百层楼摔下来没事,要你用这两个鸡蛋确定哪一层是鸡蛋可以安全落下的最高位置?

答案在这里,戳我菊花

我当时刚听完我是懵逼的,因为百度那个面试官描述的不是很清楚,我又追问了一会,才明白到底是什么意思,首先我想到的是二分法,但他又说5分法是不更好一点?我思考了一会说“好像是哈”(心中一万只草泥马路过),他又问“那10分、100分呢?是不是越多越好呢?你从中有什么体会么?”说实话我是很懵逼的,以至于直接栽倒这个题了,然后他就没有然后了,问我还有什么要问他的?我说没了,就送我到前台了,最后在送我的路上问我

  • “你知不知道你来面试的是什么岗位啊?”
  • “iOS啊”
  • “我知道是iOS,那是什么部门的你知道么?”
  • (我当时真不知道,因为HR把我推荐到别的部门了,过后查到才发现是什么质量分析评估啥的,那尼玛不就该问算法么,好吧,栽就栽了,只能说和百度无缘了,对比百度其实我还是更喜欢网易的气氛)

总之,百度面试的失利也对我挺有帮助,转头就去买了两本关于算法、数据结构的书,有些时候如果默守陈规真不一定个好事,多出来面试走走会很迅速的发现自己哪方面不足,借此,希望大家都能及时找到自己的短板,最后放一些公司被问的很多的题及答案,供大家参考:

一些常见问题

1、__block 能修改外部变量根本原因是什么?
局部变量被__block修饰后将变量的内存地址从栈区移至堆区,所以block可不可以修改变量的根本原因是内存区的不同。详情答案

2、自动释放池中的变量什么时候释放
自动释放池其实会在其作用域结束时释放池子,所有在池中的变量都会执行一次release操作,所以如果在池子中的变量执行过release操作后引用计数仍然大于0,那么就不会被释放,反之为0就会被释放,所以自动释放池中的变量也不一定出了池作用域后就会被释放的,详情可以看:详情答案

3、动态库和静态库的区别
这个我说不清楚,看大神的解答吧 详情答案

4、block根据内存区间分为几种类型?
__NSMallocBlock /__NSStackBlock/ __ NSGlobalBlock
在ARC下,默认的block创建后都是GlobalBlock,当block内部引用到外部变量的值时,就会从GlobalBlock变到NSMallocBloc,注意即使你引用的是在栈区的局部变量也是NSMallocBloc,因为ARC下会将栈区的block copy到堆区,如果在MRC下需要手动去copy才会到堆区,所以关键在于block是否引用外部的变量,外部变量内存地址决定了block的内存地址,即NSStackBlock还是NSMallocBlock。
详情答案

5、@dynamic与@synthesize的区别,使用场景?
dynamic告诉编译器不要自动创建setter、getter方法,由用户手动去实现,注意如果此时会找不到_(属性名)的实例对象,只能self.(属性名)。
synthesize如果你没有手动实现setter方法和getter方法,那么在编译的时候编译器会自动为你加上这两个方法。
如果没有声明,默认就是@synthesize 属性名 = _属性名;
使用场景:
synthesize 可以在需要同时实现set和get方法时声明 ;修改实例对象的名称等
dynamic 需要自己手动实现set和get方法时去声明,注意一定要去实现,不然调用set或get时会导致程序崩溃
参考答案

10月18日陌陌面试更新:

6、当一个tableView上面加载很多Cell,但它们同时用SDWebImage去加载同一张网络图片(即URL相同),SD的底层会请求多次网络请求吗?如果不会,它是怎么做到同一张图片下载完成后进行多次成功的回调并同时加载到Cell上呢?

说实话,SDWebImage缓存那块源码看的比较多,但下载这块只是略过看了一下,我跟面试官说我这块源码没看,他说没关系按你自己的想法怎么去实现呢?
也是出于没自信,先后说了两个方法,其中一个还真被我猜中了,但面试官没有表现出来我对的意思。

第一种:采用通知中心,这个问题就很简单了,对于同一张图片URL作为key值,在sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state这步时,就已经添加好通知并将该URL作为判断依据存入队列数组,判断当前下载队列数组里有无这个URL,如果没有执行正式的异步下载,若有表明已经在下载当中,执行等待,即return,当第一个网络请求下载好后发送关于该URL的通知告诉其余的下载队列这个已经下载完毕并将最终的UIImage作为参数回调,其他去执行UI操作即可。

第二种:利用block进行回调,对于每一个下载队列都需要知道当前的图片下载进度和下载完成的状态,即需要两个block(progressBlock、completeBlock),将每一个队列包含的block都存入数组,然后在执行下载之前还是先检查是否是第一次下载,如果是就直接进入下载队列,否则不走回调,等待第一个下载队列下载图片的完成,当下载完成时,通过URL找到存储的block数组进行回调,这样就实现了只执行一次下载任务完成多次回调。

好吧,让我们来check out一下源码,看看它到底是怎样实现的?
sd_setImage底层有一个这样的方法downloadImageWithURL:options:progress:completed:,在该方法内部会根据addProgressCallback:completedBlock:forURL:createCallback:这个方法的回调进行最终的下载任务,那么只要在这个方法里去处理是否进入下载即可,以下是该方法的实现,让我们来分析:

- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback {
    // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
    if (url == nil) {
        if (completedBlock != nil) {
            completedBlock(nil, nil, nil, NO);
        }
        return;
    }

    dispatch_barrier_sync(self.barrierQueue, ^{
        BOOL first = NO;
        if (!self.URLCallbacks[url]) {
            self.URLCallbacks[url] = [NSMutableArray new];
            first = YES;
        }

        NSMutableArray *callbacksForURL = self.URLCallbacks[url];
        NSMutableDictionary *callbacks = [NSMutableDictionary new];
        if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
        if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
        [callbacksForURL addObject:callbacks];
        self.URLCallbacks[url] = callbacksForURL;

        if (first) {
            createCallback();
        }
    });
}

先用GCD同步执行等待确保前面任务都执行完,注意URLCallbacks这个属性是一个字典,存储在SDWebImageDownloader单例中,图片的url作为key,由于同一张图片可能在多处加载,所以callbackBlock可能会有很多,也验证了我第二个思路,这里用的Array用来存储(即[callbacksForURL addObject:callbacks];用来添加每一个队列里的回调block),在这段代码的最后可以看到,用一个first局部变量表示是否是第一次下载

       if (!self.URLCallbacks[url]) {
            self.URLCallbacks[url] = [NSMutableArray new];
            first = YES;
        }

如果是则回调completedBlock告诉管理器这是第一次对于该URL的下载,你可以开始下载了。

       if (first) {
            createCallback();
        }

虽然在二面的时候被拒了,面试官说我基础知识不牢固。虽无缘陌陌,但这次的面试经历让我又找到了些自己的不足(有些说对了,又因为不自信改了答案)

10.19日晚更新

Bad News
网易给答复说最终没有录用我 很遗憾 无缘网易
心情有一点小失落 又得继续开始找工作了 好吧 相信属于我的那个公司还在等我 千万不要气馁 以此鼓励自己

[图片上传失败…(image-3520aa-1513048180203)]

写在后面

接下来面试我会随时进行补充,希望还在找工作的朋友们,大家都能够做到从容不迫找到心中理想的公司。

    原文作者:被帅醒的小吴同志
    原文地址: https://www.jianshu.com/p/057651fa44e8
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞