iOS 阿里三面 面试题整理

阿里三面面试题:

  • 1.dSYM你是如何分析的?
  • 2.多线程有哪几种?你更倾向于哪种?
  • 3.单例弊端?
  • 4.如何把异步线程转换成同步任务进行单元测试?
  • 5.介绍下App启动的完成过程?
  • 6.好比App启动过慢,你可能想到的因素有哪些?
  • 7.0x8badf00d表示是什么?
  • 8.怎么防止反编译?
  • 9.说说你遇到到的技术难点?
  • 10.说说你了解的第三方原理或底层知识?

一、dSYM你是如何分析的

方法1 使用XCode

这种方法多是最容易的方法了。git

  1. 要使用Xcode符号化 crash log,你须要下面所列的3个文件:
  2. crash报告(.crash文件)
  3. 符号文件 (.dsymb文件)
  4. 应用程序文件 (appName.app文件,把IPA文件后缀改成zip,而后解压,Payload目录下的appName.app文件), 这里的appName是你的应用程序的名称。
  5. 把这3个文件放到同一个目录下,打开Xcode的Window菜单下的organizer,而后点击Devices tab,而后选中左边的Device Logs。
  6. 而后把.crash文件拖到Device Logs或者选择下面的import导入.crash文件。
  7. 这样你就能够看到crash的详细log了。

方法2 使用命令行工具symbolicatecrash

  1. 有时候Xcode不可以很好的符号化crash文件。咱们这里介绍如何经过symbolicatecrash来手动符号化crash log。
  2. 在处理以前,请依然将“.app“, “.dSYM”和 ".crash"文件放到同一个目录下。如今打开终端(Terminal)而后输入以下的命令:
  3. export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  4. 而后输入命令:
  5. /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash appName.crash appName.app > appName.log
  6. 如今,符号化的crash log就保存在appName.log中了。

方法3 使用命令行工具atos

  1. 若是你有多个“.ipa”文件,多个".dSYMB"文件,你并不太肯定到底“dSYMB”文件对应哪一个".ipa"文件,那么,这个方法就很是适合你。
  2. 特别当你的应用发布到多个渠道的时候,你须要对不一样渠道的crash文件,写一个自动化的分析脚本的时候,这个方法就极其有用。
  3. 具体方法 请百度

本文分析了拿到用户的.crash文件以后,如何符合化crash文件的3种方法,分别有其适用场景,方法3适用于自动化crash文件的分析。github

2.多线程有哪几种?你更倾向于哪种?

  1. NSThread
  2. Cocoa NSOperation (使用NSOperation和NSOperationQueue)
  3. GCD (Grand Central Dispatch)

1.NSThread:(两种建立方式)面试

[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];

NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];

[myThread start];

复制代码

优势:NSThread 比其余两个轻量级。 缺点:须要本身管理线程的生命周期,线程同步,线程同步时对数据的加锁会有必定的系统开销。算法

2.Cocoa Operationsql

NSOperationQueue*oprationQueue= [[NSOperationQueuealloc] init];

oprationQueueaddOperationWithBlock:^{

//这个block语句块在子线程中执行

}
复制代码

优势:不须要关心线程管理,数据同步的事情。 Cocoa Operation 相关的类是 NSOperation ,NSOperationQueue。NSOperation是个抽象类,使用它必须用它的子类,能够实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。建立NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行,咱们会把咱们的执行操做放在NSOperation中main函数中。数据库

3.GCD Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法,GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。它让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务,一个任务能够是一个函数(function)或者是一个block。 dispatch queue分为下面三种: private dispatch queues,同时只执行一个任务,一般用于同步访问特定的资源或数据。 global dispatch queue,能够并发地执行多个任务,可是执行完成的顺序是随机的。 Main dispatch queue 它是在应用程序主线程上执行任务的。 GCD 扫盲篇;编程

三、单利的弊端

优势: 1:一个类只被实例化一次,提供了对惟一实例的受控访问。 2:节省系统资源 3:容许可变数目的实例。缓存

缺点: 1:一个类只有一个对象,可能形成责任太重,在必定程度上违背了“单一职责原则”。 2:因为单利模式中没有抽象层,所以单例类的扩展有很大的困难。 3:滥用单例将带来一些负面问题,如为了节省资源将数据库链接池对象设计为的单例类,可能会致使共享链接池对象的程序过多而出现链接池溢出;若是实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将致使对象状态的丢失。bash

四、如何把异步线程转换成同步任务进行单元测试?

参考此篇博客 里面的信号量的解释,Dispatch Semaphore 信号量 在项目中的应用:强制把异步任务转换为同步任务来方便进行单元测试网络

五、介绍下App启动的完成过程?

  • ①.先加载Main函数

  • ②.在Main函数里的 UIApplicationMain方法中,建立Application对象 建立Application的Delegate对象

  • ③.建立主循环,代理对象开始监听事件

  • ④.启动完毕会调用 didFinishLaunching方法,并在这个方法中建立UIWindow

  • ⑤.设置UIWindow的根控制器是谁

  • ⑥.若是有storyboard,会根据info.plist中找到应用程序的入口storyboard并加载箭头所指的控制器

  • ⑦.显示窗口

本文考虑的时步骤③以后到步骤⑦结束时将要调用的方法

其中有AppDelegate,ViewController,MainView(控制器的View),ChildView(子控件的View)的18个方法


AppDelegate中的:

  • 1.application:didFinishLaunchingWithOptions:

  • 2.applicationDidBecomeActive:

ViewController中的:

  • 3.loadView

  • 4.viewDidLoad

  • 5.load

  • 6.initialize

  • 7.viewWillAppear

  • 8.viewWillLayoutSubviews

  • 9.viewDidLayoutSubviews

  • 10.viewDidAppear

MainView(控制器的View)中的

  • 11.initWithCoder(若是没有storyboard就会调用initWithFrame,这里两种方法视为一种)

  • 12.awakeFromNib

  • 13.layoutSubviews

  • 14.drawRect

ChildView(子控件View)中的:

  • 15.initWithCoder(若是没有storyboard就会调用initWithFrame,这里两种方法视为一种)

  • 16.awakeFromNib

  • 17.layoutSubviews

  • 18.drawRect

那么问题来了,不往下看你能够把上面的十八个方法排个顺序么?

[图片上传失败...(image-666d9e-1511926275309)]

+ (void)load; //这是应用程序启动就会调用的方法,在这个方法里写的代码最早调用
复制代码
+ (void)initialize; //这个是须要用到本类时才调用,这个方法里通常写设置导航控制器的主题啊之类的,
//若是在后面的方法设置导航栏主题就晚了!(固然在上面的方法里也能写)
复制代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
//这个方法里面会建立UIWindow,设置根控制器并展示,
//好比某些应用程序要加载受权页面也是在这加,也能够设置观察者,监听到通知切换根控制器
复制代码
ChildView - (instancetype)initWithCoder:(NSCoder *)aDecoder;
//这里反正我是万万没想到,childView的initwithcoder会在MainView的方法以前调用,
//父的都还没出来,就先整子控件? 有了解比较透彻的博友恳请告诉我谢谢。
复制代码
MainView - (instancetype)initWithCoder:(NSCoder *)aDecoder;
// 就是关于应用程序的数据存储后的解档操做。
复制代码
MainView - (void)awakeFromNib;
//在这个方法里设置view的背景等一系列普通操做,不要写关于frame的还不许,
//在使用IB的时候才会涉及到此方法的使用,当.nib文件被加载的时候,
//会发送一个awakeFromNib的消息到.nib文件中的每一个对象,
//每一个对象均可以定义本身的awakeFromNib函数来响应这个消息,执行一些必要的操做。 
复制代码
ChildView - (void)awakeFromNib
//子控件也有本方法,重写父类的方法。基本用法同上 
复制代码
- (void)loadView; 
//建立视图的层次结构,这里须要注意,
//在没有建立控制器的view的状况下不能直接写 self.view 由于self.view的底层是:
    if(_view == nil){
     _view = [self loadView]
    }
//因此这么写会直接形成死循环。
//若是重写这个loadView方法里面什么都不写,会显示黑屏。
复制代码
- (void)viewDidLoad;
//卧槽,这个方法是用的最多的方法,可是在以后的开发中就会发现愈来愈不靠谱,
//不少东西都还没加载完毕,各类取值都不许确,不多在这里面写东西了。 
//这里只是把视图元件加载完成
复制代码
- (void)viewWillAppear:(BOOL)animated;
//视图将要出现,这个方法用的很是多,好比若是要设置导航栏的setNavigationBarHiden:animate: 
//就必需要在这里写,才能完美契合,不卡跳。 还有不少好比监听屏幕旋转啦,

//viewWillTransitionToSize:可能要在本方法里再调一次,
//或者就是新到这个界面要reloadData或是自动下拉刷新等 都是写在本方法里
复制代码
- (void)viewWillLayoutSubviews;
//视图将要布局子视图,苹果建议的设置界面布局属性的方法,
//这个方法和viewWillAppear里,系统的底层都是没有写任何代码的,也就是说这里面不写super 也是能够的
复制代码
MainView  - (void)layoutSubviews;
//在这个方法里通常设置子控件的frame,由于这里至关因而布局基本完成了,
//设置时取到的frame或者是self.bounds才最准,若是在awakeFromeNib里写会不许确 。
//还有这里要切记千万不能把super layoutSubviews忘了,可能最后都很难找到这个bug
复制代码
- (void)viewDidLayoutSubviews;
//这个方法我也是玩玩没想到,控制器的view的子控件尚未布局好呢,怎么这个控制器就已经说布局所有完成了?
//那后边的布局就不等了? 有独到看法的也恳请你告诉我,这其中苹果的意思究竟是什么。 
复制代码
ChildView - (void)layoutSubviews;
//控制器的子控件里的子控件的布局就在这里写了。 
复制代码
MainView - (void)drawRect:(CGRect)rect;
//由于默认全部额UI控件都是画上去的,在这一步就是把全部的东西画上去,
//有时候须要用到Quartz2D的知识的时候都是在这个方法里话,但也是要注意别忘了写super,
//否则系统本来的东西就都画不上来了,这里要建议尽量使用贝塞尔路径画图形,
//由于系统默认的那个上下文画法有时可能会内存泄露。drawRect方法只能在加载时调用一次,
//若是后面还须要调用,好比下载进度的圆弧,须要一直刷帧,
//就要使用setNeedsDisplay来定时屡次调用本方法
复制代码
ChildView - (void)drawRect:(CGRect)rect;
//view的子控件内部的画图方法,有时能够本身自定义label 中间带个删除线的(用来写打折前的原价) 就是在这里画根线 。
复制代码
- (void)viewDidAppear:(BOOL)animated;
//把上面的画图都画完了,这里就会显示,视图彻底加载完成。
//在这里的操做可能就是设置页面的一些动画,或者是设置tableView,collectionView,
//QQ聊天页面啥的滚动到底部scrollToIndexPath之类的代码操做。
复制代码
- (void)applicationDidBecomeActive:(UIApplication *)application;
//最后这是AppDelegate的应用程序获取焦点方法,真正到了这里,才是全部东西所有加载完毕,应用程序整装待发保持最佳状态等待用户操做。
//这个方法中通常会写关于弹出键盘的方法,好比有的用户登陆界面为了更好的用户体验,
//就让你在刚打开程序来到登陆界面的时候,光标的焦点就自动在帐号的文本框里闪烁,
//也就是设置帐号文本框为第一响应者。键盘在页面加载完毕后从下方弹出,这种代码通常就在本方法写。
复制代码

六、好比App启动过慢,你可能想到的因素有哪些?

1. App启动过程
  1. 解析Info.plist
    • 加载相关信息,例如如闪屏
    • 沙箱创建、权限检查
  2. Mach-O加载
    • 若是是胖二进制文件,寻找合适当前CPU类别的部分
    • 加载全部依赖的Mach-O文件(递归调用Mach-O加载的方法)
    • 定位内部、外部指针引用,例如字符串、函数等
    • 执行声明为__attribute__((constructor))的C函数
    • 加载类扩展(Category)中的方法
    • C++静态对象加载、调用ObjC的 +load 函数
  3. 程序执行
    • 调用main()
    • 调用UIApplicationMain()
    • 调用applicationWillFinishLaunching
二、影响启动性能的因素
  1. main()函数以前耗时的影响因素

    • 动态库加载越多,启动越慢。
    • ObjC类越多,启动越慢
    • C的constructor函数越多,启动越慢
    • C++静态对象越多,启动越慢
    • ObjC的+load越多,启动越慢
  2. main()函数以后耗时的影响因素

    • 执行main()函数的耗时
    • 执行applicationWillFinishLaunching的耗时
    • rootViewController及其childViewController的加载、view及其subviews的加载

另外参考一下今日头条的启动优化方案

,针对于今日头条这个App咱们能够优化的点以下:

  • 纯代码方式而不是storyboard加载首页UI。
  • 对didFinishLaunching里的函数考虑可否挖掘能够延迟加载或者懒加载,须要与各个业务方pm和rd共同check 对于一些已经下线的业务,删减冗余代码。
  • 对于一些与UI展现无关的业务,如微博认证过时检查、图片最大缓存空间设置等作延迟加载。
  • 对实现了+load()方法的类进行分析,尽可能将load里的代码延后调用。
  • 上面统计数据显示展现feed的导航控制器页面(NewsListViewController)比较耗时,对于viewDidLoad以及viewWillAppear方法中尽可能去尝试少作,晚作,不作。

七、0x8badf00d表示是什么?

看门狗超时,在iOS上,它常常出如今执行一个同步网络调用而阻塞主线程的状况。所以,永远不要进行同步网络调用。

八、怎么防止反编译?

  1. 本地数据加密。
    • iOS应用防反编译加密技术之一:对NSUserDefaults,sqlite存储文件数据加密,保护账号和关键信息
  2. URL编码加密。
    • iOS应用防反编译加密技术之二:对程序中出现的URL进行编码加密,防止URL被静态分析
  3. 网络传输数据加密。
    • iOS应用防反编译加密技术之三:对客户端传输数据提供加密方案,有效防止经过网络接口的拦截获取数据
  4. 方法体,方法名高级混淆。
    • iOS应用防反编译加密技术之四:对应用程序的方法名和方法体进行混淆,保证源码被逆向后没法解析代码
  5. 程序结构混排加密。
    • iOS应用防反编译加密技术之五:对应用程序逻辑结构进行打乱混排,保证源码可读性降到最低

其实我以为大可没必要,自己反编译成本就很大,代码这么多,一个个反编译过来是在蛋疼,就算有伪代码也须要理解,并且有些代码就算有伪代码也很难理解。

只要作好核心代码,作好混淆就好了,好比涉及到密码,核心算法。

九、说说你遇到的技术难点并如何解决的?

通常这种问题的时候,面试官是想经过你讲解你遇到的是什么类型的问题,而后是怎么解决的方案来判断你的知识层面。值得注意的一点是,每每很多同窗在回答这道问题讲如何解决的时候会说:首先经过Google、百度等搜索寻找解决方案,二、问群里。。。等等之类的答案。敲黑板啦~~注意啦,同窗们,人家要的不是这个答案好吗?虽然你这么答也不算是错,只是理解有误差,但起不到加分的结果。因此在面试以前,各位同窗应该好好的想一想这个问题的答案,固然每一个人遇到的难点都不大相同,涉及到的技术层面也不同,这也是这个问题的目的所在。

十、说说你了解的第三方原理或底层知识?

自行发挥吧!

相关文章
相关标签/搜索