iOS开发者跟你谈谈面试技术中的坑

640?wx_fmt=gif

640?wx_fmt=jpeg

Linux编程 点击右侧关注,免费入门到精通! 640?wx_fmt=jpeg


作者|胖虎-callmeV

https://www.jianshu.com/p/103e3cfaac34


在这个技术人才饱满的时代,面试基本上是很重要的一关了,而这个时候就要懂得HR看中你的哪些方面了,技术、人品、经验。能否从容的应对面试官所给出的道道难题,是能否成功拿到offer的重点.


今天就来说说关于简历的书写和面试中被问到的APP崩溃和内存泄漏两个问题!!!


640?wx_fmt=gif简历


简历就是简单的陈述你的经历,不可做的太过于啰嗦,关键的地方要是你做过的项目,并介绍项目里的技术点和实现的功能,这里推荐智联的简历模板。


有些同学刚刚毕业项目经验很少可以填写一些自己在校的编程比赛经历、实习项目等等。其次一些好的兴趣和活动经历是可以反应一个人的人品的,不要忽视一些小的细节,比如在上一家公司工作的时间,一般少于一年的就不要往上写了,这会令HR感到你的稳定性很差,招你进来会对公司造成损失的。


好好过自己,牌已经发给你了,就看你如何打!


玄学,人都是需要一些运气的,面试也不能逃脱,有的公司可能只是为了解决一个技术问题并不真正的招聘,有的却是真正的急需招人,前者你技术再好也进不去,后者倒可以不是那么艰难!


iOS开发者跟你谈谈面试技术中的坑_第1张图片


下面来分享一些面试总结的面试题,希望有帮助!!!


640?wx_fmt=gifAPP崩溃,启动秒退


在新 iOS 上正常的应用,到了老版本 iOS 上秒退最常见原因是系统动态链接库或Framework无法找到。


这种情况通常是由于 App 引用了一个新版操作系统里的动态库(或者某动态库的新版本)或只有新 iOS 支持的 Framework,而又没有对老系统进行测试,于是当 App 运行在老系统上时便由于找不到而秒退。解决办法是等开发人员发现这个问题后升级程序,或由用户自行升级其操作系统。


还有一种常见的秒退是程序在升级时,修改了本地存储的数据结构,但是对用户既存的旧数据没有做好升级,结果导致初始化时因为无法正确读取用户数据而秒退。


这类问题通常只需删除程序后重新安装一遍就能解决。但缺点是用户的既存数据会丢失——就算有备份可能也无济于事,因为备份下来的旧数据还是无法被正确升级。


还有一类秒退或是用到 App 里某个功能后必退的原因,是开发时用到了只有新版操作系统才支持的某个方法,而又没有对该方法是否存在于老系统中做出判断。


例如程序启动时用到了 Game Center,而没有判断用户的机器是否支持 Game Center,于是就秒退了。


640?wx_fmt=gif访问的数据为空或者类型不对


这类情况是比较常见的,后端传回了空数据,客户端没有做对应的判断继续执行下去了,这样就产生了crash。或者自己本地的某个数据为空数据而去使用了。


还有就是访问的数据类型不是期望的数据类型而产生崩溃。比如,我期望服务端返回string类型,但是后台给我返回的是NSNumber类型,那么我操作时候用到的是string的方法。结果因为找不到对应的方法而崩溃。


解决办法:1、服务端都加入默认值,不返回空内容或无key。或者是在客户端进行非空判断。2、对容易出错的地方提前进行类型判断。


640?wx_fmt=gif点击事件方法处理不当


这类情况比较常见,比如我的点击事件需要对传入的参数做处理,但是点击时,我传入的对象类型或者传入为空,等到参数进行处理的时候,由于方法不当,产生crash。


640?wx_fmt=gif数组越界


当客户端尝试对数组中的数据进行操作的时候,数组为空或者所做的操作index 超过数组本身范围,就会引起崩溃。


640?wx_fmt=gif下拉刷新时崩溃


使用下拉刷新时,如果下拉的距离长了就会崩溃。原因是,在下拉刷新请求数据之前就将本地数组清空了。分析,下拉刷新的逻辑:1、下拉 2、下拉达到临界值时触发网络请求 3、等待数据加载到本地以后才更新datasource 4、tableview reloadData。 


如果先清空数组再下拉请求,后果就是往下拉的距离超过一个 cell 的高度时,table view 的几个委托方法就会被调用,由于 data source 已经被清空,造成错误的内存访问(包括数组越界,访问已销毁的对象)导致 crash。


640?wx_fmt=gif操作了不该操作的对象、野指针


iOS中有空指针和野指针两种概念。


空指针是没有存储任何内存地址的指针。如Student s1 = NULL;和Student s2 = nil;


而野指针是指指向一个已删除的对象("垃圾"内存既不可用内存)或未申请访问受限内存区域的指针。野指针是比较危险的。因为野指针指向的对象已经被释放了,不能用了,你再给被释放的对象发送消息就是违法的,所以会崩溃。


野指针访问已经释放的对象crash其实不是必现的,因为dealloc执行后只是告诉系统,这片内存我不用了,而系统并没有就让这片内存不能访问。


所以野指针的奔溃是比较随机的,你在测试的时候可能没发生crash,但是用户在使用的时候就可能发生crash了。


注意:arc环境比非arc环境更少出现野指针。


1、对象释放后内存没被改动过,原来的内存保存完好,可能不Crash或者出现逻辑错误(随机Crash)。


2、对象释放后内存没被改动过,但是它自己析构的时候已经删掉某些必要的东西,可能不Crash、Crash在访问依赖的对象比如类成员上、出现逻辑错误(随机Crash)。


3、对象释放后内存被改动过,写上了不可访问的数据,直接就出错了很可能Crash在objc_msgSend上面(必现Crash,常见)。


4、对象释放后内存被改动过,写上了可以访问的数据,可能不Crash、出现逻辑错误、间接访问到不可访问的数据(随机Crash)。


对象释放后内存被改动过,写上了可以访问的数据,但是再次访问的时候执行的代码把别的数据写坏了,遇到这种Crash只能哭了(随机Crash,难度大,概率低)!!


5、对象释放后再次release(几乎是必现Crash,但也有例外,很常见)


640?wx_fmt=gif内存处理不当


用instruments排查内存泄露问题


主线程UI长时间卡死,被系统杀掉


主线程被卡住是非常常见的场景,具体表现就是程序不响应任何的UI交互。这时按下调试的暂停按钮,查看堆栈,就可以看到是到底是死锁、死循环等,导致UI线程被卡住。


多线程之间切换访问引起的crash


多线程引起的崩溃大部分是因为使用数据库的时候多线程同时读写数据库而造成了crash。


640?wx_fmt=gifARC内存泄漏


block系列


在 ARC 下,当 block 获取到外部变量时,由于编译器无法预测获取到的变量何时会被突然释放,为了保证程序能够正确运行,让 block 持有获取到的变量,向系统显明:我要用它,你们千万别把它回收了!然而,也正因 block 持有了变量,容易导致变量和 block 的循环引用,造成内存泄露!


delegate系列


说白了就是循环使用的问题,假如我们是写的strong,那么 两个类之间调用代理就是这样的啦


 
   
 
   

 BViewController *bViewController = [[BViewController alloc] init];
bViewController.delegate = self//假设 self 是AViewController
[self.navigationController pushViewController:bViewController animated:YES];

/**
 假如是 strong 的情况
    bViewController.delegate ===> AViewController (也就是 A 的引用计数 + 1)
    AViewController 本身又是引用了  ===> delegate 引用计数 + 1
 导致: AViewController  Delegate ,也就循环引用啦
 */


/**
假如是 strong 的情况


bViewController.delegate ===> AViewController (也就是 A 的引用计数 + 1)
AViewController 本身又是引用了  ===> delegate 引用计数 + 1
导致: AViewController  Delegate ,也就循环引用啦


*/

Delegate创建并强引用了 AViewController;(strong ==> A 强引用、weak ==> 引用计数不变)


所以用 strong的情况下,相当于 Delegate 和 A 两个互相引用啦,A 永远会有一个引用计数 1 不会被释放,所以造成了永远不能被内存释放,因此weak是必须的。


performSelector 系列


由于动态,编译器不知道即将调用的 selector 是什么,不了解方法签名和返回值,甚至是否有返回值都不懂,所以编译器无法用 ARC 的内存管理规则来判断返回值是否应该释放。因此,ARC 采用了比较谨慎的做法,不添加释放操作,即在方法返回对象时就可能将其持有,从而可能导致内存泄露。


640?wx_fmt=gifNSTimer


NSTimer会造成循环引用,timer会强引用target即self,在加入runloop的操作中,又引用了timer,所以在timer被invalidate之前,self也就不会被释放。

所以我们要注意,不仅仅是把timer当作实例变量的时候会造成循环引用,只要申请了timer,加入了runloop,并且target是self,虽然不是循环引用,但是self却没有释放的时机。


 推荐↓↓↓ 

640?wx_fmt=png

?16个技术公众号】都在这里!

涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。

640?wx_fmt=png万水千山总是情,点个 “ 好看” 行不行

你可能感兴趣的:(iOS开发者跟你谈谈面试技术中的坑)