最近觉得现在所在公司平台用户量太少,自身技术已经到了一个瓶颈,是时候需要换一个用户量多的平台,好好研究下iOS的性能优化、内存优化等问题了。
所面试的公司由于一些默认的规定,就不多说了,大致是面了一个下午,从2点到下午6点半,正式拿到offer,先从笔试题开始说起吧。
笔试题:15道题,稍微有些多了,做了将近一个半小时。从笔试题就可以稍微看出一个公司的iOS团队技术水平如何,以及他们是不是真的想要招人。我春节之前投过一波简历,当时挑了一家公司去,还没技术面,只是看了他们的面试题,就知道了这个公司大概不会录用我,因为那面试题太水了,一看就是百度过来还没有筛选的。做完之后,面我的面试官就和我说,这套面试题从他入职以来到现在就没换过,我就呵呵了,明显是没招人的打算,叫我过来纯属浪费时间。
第一道,是C\OC\C++里面的一些常识吧,就是nil、Null、Nil的区别
这道题我答的不好,只是从C\OC语法的角度上解释了下它们的区别,回来百度下,看了其他人的回答,该这么写:
nil和null从字面意思来理解比较简单,nil是一个对象,而NULL是一个值,我的理解为nil是将对象设置为空,而null是将基本类型设置为空的,个人感觉有点像属性当中,基本类型分配为assign NSString类型一般分配copy,而对象一般用retain。而且我们对于nil调用方法,不会产生crash或者抛出异常。
nil是一个对象指针为空,Nil是一个类指针为空,NULL是基本数据类型为空。这些可以理解为nil,Nil, NULL的区别吧。
第二道,UIEvent与UITouch的异同
我当时的回答是,UIEvent对象包含一个UITouch对象,这是错误的。实际开发中,我经常用到touches系列API,但是基本没关注UIEvent对象:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [[touches anyObject] locationInView:self.view]; }
经常这么用,主要关注的是UITouch对象,谷歌过来,关于UITouch:
当用户手指触摸屏幕,就会创建一个与这根手指相关联的对象,一根手指对应一个UITouch对象,它保存着与手指相关的信息,比如手指触摸的位置、时间、阶段。
UIEvent对象:
每产生一个事件,就会产生一个UIEvent对象,它称为事件对象,记录事件产生的时刻和类型。
第三道,响应者链条,这是妥妥的送分题,不详细解释了。
第四道,好像是关于assign\copy\retain\nonatomic\atomic的使用场合吧,也算是送分题,需要注意的是,即使是atomic也不能保证线程安全的,这个我测试过,相应的资料谷歌下,应该有的。
第五道,好像是为什么delegate用assign,而不用retain,送分题,不解释。
第六道,就是说,一个autoreleasepool在什么时候被销毁?
如果不知道RunLoop机制,这里建议回答,在线程被销毁之前会销毁autoreleasepool,对pool立面的所有对象做一次release操作。
正确的回答,应该是在RunLoop进入休眠之前,会销毁pool,在RunLoop被唤醒时,会创建pool,但是,你既然这么写了,就要做好面试官问你RunLoop相关知识的准备。
第七、八、九、十道,基本就是MRC的内存管理,判断是否正确以及纠错。
比如说:
NSString *name = [[NSString alloc] initWithString:@"lisi"]; [name retain]; [name release]; [name autorealse];
需要在后面写出reationCount的值,一开始我比较蒙的是,autorealse到底在啥时候会对name进行一次release操作,然后想了下,要进行release操作,肯定是要这个RunLoop进入休眠时,而在这样一个状态,RunLoop并未进入休眠......
实际上也证明我是正确的:Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当 前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。那么pool在啥时候被销毁?上面那个题目已经有解释了。
第十一、十二、十三道,貌似忘记了,不是送分题,但也没有很大难度的题,没啥印象了。
第十四、十五道,是算法题,第十四题,我没看懂出题人的意思?!看了好几遍,没看懂,唯一空白的一个题目。
第十五题,是将两个有序链表合并成一个有序链表,分分钟秒了它。
第一面,应该是iOS负责人。
大概聊了将近两个小时吧,挺认真负责的,技术非常好。他所提的比较有印象的面试问题:
1、RunLoop,于是我便把我理解的RunLoop详细说了说,大致就是下面这些东西:
RunLoop:运行循环,一个iOS程序的从main函数进入UIApplicationMain,UIApplicationMain就开启了一个RunLoop,这个RunLoop是主线程的RunLoop,也就是Main RunLoop,它的作用是让我们的OC程序一直运行。 实际上,我们可以把RunLoop认为是一个死循环,它在这个循环内,不断的休眠与重启,从而保持程序的运行,处理程序的各种事件。例如: do { }while(1); iOS中有两套API来使用和访问RunLoop,一个是Foundation下的NSRunLoop,一个是Core Foundation下的CFRunLoopRef,(NSRunLoop是基于CFRunLoopRef的一层OC包装)。 RunLoop与线程的关系: 每一个线程都有与之相对应的RunLoop,只有主线程的RunLoop是默认开启的,子线程的RunLoop需要我们手动开启。 RunLoop在第一次获取时创建,在线程结束时销毁。 [NSRunLoop CurrentRunLoop] [NSRunLoop MainRunLoop] CFRunLoopGetCurrent() CFRunLoopGetMain() Core Foundation中runLoop相关的五个类 CFRunLoopModeRef: 代表着RunLoop的运行模式,RunLoop在一个时间点只能运行一种模式,比如说要么是defaultMode,要么是TrackingMode 一个RunLoop包含若干个Mode,每个mode又包含若干个Source\Timer\observer 如果需要切换Mode,只能是先退出Loop,再重新指定一个Mode进入 这样做的目的主要是为了分割开不同组的Source/Timer/Observe,让他们互不影响。 CFRunLoopSourceRef :系统的一些点击、拖拽等事件,相当于一个事件源 CFRunLoopTimerRef : 定时器相关的处理 CFRunLoopObserverRef : 实际上,我们是可以监听RunLoop的活动的,比如说监听它是否开启、是否将要进入休眠等。 这两种RunLoop使我们在日常编程中经常用到的: kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行 UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响 在OC中,几种常见的Mode NSDefaultRunLoopMode UITrackingRunloopMode : 需要注意,这种模式下的Mode,是只能处理UIScrollView、或者继承自UIScrollView的对象的拖拽等事件的,也就是说,当UIScrollView在滚动的时候,是进入UITrackingRunloopMode,在默认条件下,Timer是添加在NSDefaultRunLoopMode下的,所以就不能处理Timer的相关事件。 (利用这样的一个特性,我们可以优化UITableView和UICollectionView的滚动,我们可以将图片的加载设置在NSDefaultRunLoop下,这样在滚动UITableView的时候,RunLoop会是UITrackingRunloopMode,就不会去加载图片,从而减少卡顿的现象出现,使得UI交互更加流畅) 还有一个NSRunLoopCommonMode,需要注意的是,它并不是真正的Mode,只是一个占位符 如果想要监听RunLoop状态的改变,有如下代码可以设置监听: - (void)observer { //创建observer CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { NSLog(@"监听到RunLoop状态发生改变 %zd", activity); }); //添加监听者 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); CFRelease(observer); } 这是runLoop可以被监听的状态: /* Run Loop Observer Activities */ typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0), //进入,也就是开启runloop了的时候 kCFRunLoopBeforeTimers = (1UL << 1), //在处理所有定时器操作之前 kCFRunLoopBeforeSources = (1UL << 2), //在处理所有source源(点击等事件)之前 kCFRunLoopBeforeWaiting = (1UL << 5), //将要进入休眠状态 kCFRunLoopAfterWaiting = (1UL << 6), //将要苏醒的状态 kCFRunLoopExit = (1UL << 7), //退出runLoop kCFRunLoopAllActivities = 0x0FFFFFFFU //以上所有的状态 }; @autoReleasePool在什么时候被释放? 它是在它所在的runLoop进入休眠时被释放,在runLoop再一次启动时创建。
当然,这些都是理论上的知识,实际中我们可以把imageView设置成在RunLoop的DefaultMode下显示,那么当UIScrollView,以及继承自UIScrollView的控件在拖拽的时候就不会显示ImageView,因为在滚动的时候,RunLoop是TrackingMode。
还可以开启一条常驻线程,这样就没必要多次创建子线程,造成性能上的损耗。
2、如果我们在开发中使用SDWebImage框架,会发现在UITableView滚动的停止的时候,它是优先下载显示在屏幕上的cell里面的图片的,这个效果如果让你来实现,你如何实现?
开发中我是经常使用SDWebImage框架的,但是没看过它的源代码,不过以前我做过不依赖SDWebImage框架的图片下载操作,那个时候是用NSOperation来实现的,还考虑到了重复下载的问题。
NSOperation是可以设置停止(暂停)的,这样的话,监听scrollView的滚动,当cell离开屏幕时,我们就把它设置为暂停下载状态,当它出现在屏幕时,再设置为开启下载状态。
面试官问我,是否还有其他的解决方案,(一脸蒙--)没有了。然后他提出了一种很好的解决方案,就是我们在设置并行队列的时候,是有一个优先值的,我们设置后面的image下载操作的优先级递增,这样的话,轻轻松松就解决了问题。
(好吧,我一直都在使用着默认的优先级,的确没考虑过这个问题,不过苹果文档也说了,建议开发者使用default优先级,因为如果优先级比较乱的话,可能会造成未知原因而导致程序崩溃,但这并不是理由,不知道就是不知道)。
3、问了下iOS里面工厂模式的使用场景,其实这个问题我在《Objective-C编程之道》上看到过,也知道它,但是那一瞬就是脑海一片空白,好吧,然后面试官和我说了,比如UIButton的类方法创建,里面是黑箱的,我们只是知道它会返回一个啥样类型button回来,类似的,NSValues等。(不得不说是幸运啊,刚好这个问题,在移动端负责人面我时候,我就举了这样的例子)
4、有三个网络请求需要发送,等三个网络请求发送完之后,要更新UI,怎么操作可以节省时间?
我第一个回答,觉得是直接使用dispatch_group来搞就好,不过面试官提醒了下我,就是一个网络请求发送完毕之后,等待数据返回还要一段时间,所以,dispatch_group是不行的。
然后,我觉得貌似只能一条条发送了,认真的想了下,其实是可以设置一个int属性,来记录是否都返回数据成功了,如果成功,就++,然后用kvo来监听这个属性就好。(当时回答是这么回答的,实际上应该用不到kvo来监听,可以直接重写该属性的setter方法)
还有其他的问题吧,不过印象不是很深刻了。
移动端负责人面试:
1、至此,第一面算是过了,心中松了一口气。这位面试官进来,先是问了我,iOS程序启动这之间的一系列过程,我回答的比较简单,从main->UIApplicationMain来开启一个主运行循环->Main RunLoop 开始处理各种source\observe\timer,从一个Mode到另一个Mode,然后调用appDelegate里面的application didFinish....那方法,进入KeyWindow......
2、把你所知道的设计模式其中思想都说说,举例说下应用场景
差点跪了,以前在看设计模式的时候,那理论太多了,记不住啊。然后只说出九种模式的思想,勉强算是过关了。
3、把所知道的排序算法手写出来
我在想,还好不是面试官来定某些算法,我所知道的并且能写的,冒泡、选择、快速、归并,写了下,算是过关了。
技术总监\经历面试
就是一个算法题,一个人上台阶,一次可以走一阶,走两阶,走三阶,那么如果有n阶,走法有多少种?
递推题,根据前面的规律,可以很容易推出,f(n) = f(n-1) + f(n-2) +f(n-3),然后用c语言把它写出来了。
之后就是人事面,顺利拿到offer。
ps:我辞职报告已经提交了,公司让我帮他面试iOS人员,我想说,那些培训的,写两年iOS工作经验,竟然和我说没学过C?对C不熟?作为一个计算机专业毕业的人,做数据结构的题,20道只正确7道,我去。这一行现在是有多乱,开的工资,10~~15k,还有一个15~~25k!真的是无知者无畏么?