前段时间更新了一篇 给iOS中高级面试官的一份招聘要求 收到不少小伙伴的点赞与关注。可能有不少小伙伴已经带着我在那篇文章给你们提供的一些面试技巧 & 其中的面试题 已经开始招聘或者应聘了!这里应你们要求,对里面的面试题提供相关答案!相信不管是面试官仍是求职者都是有所收获的~~
PS:篇幅有点长,你们能够关注或者点赞收藏以备不时之需!!!html
1:讲讲你对atomic & nonatomic的理解ios
2:被 weak 修饰的对象在被释放的时候会发生什么?是如何实现的?知道sideTable 么?里面的结构能够画出来么?git
被weak修饰的对象在被释放时候会置为nil,不一样于assign;github
Runtime 维护了一个 weak表,用于存储指向某个对象的全部 weak指针。weak表 实际上是一个 hash(哈希)表,Key 是所指对象的地址,Value 是 weak指针 的地址(这个地址的值是所指对象指针的地址)数组。web
struct SideTable {
// 保证原子操做的自旋锁
spinlock_t slock; // 引用计数的 hash 表 RefcountMap refcnts; // weak 引用全局 hash 表 weak_table_t weak_table; } struct weak_table_t { // 保存了全部指向指定对象的 weak 指针 weak_entry_t *weak_entries; // 存储空间 size_t num_entries; // 参与判断引用计数辅助量 uintptr_t mask; // hash key 最大偏移值 uintptr_t max_hash_displacement; };
3:`block` 用什么修饰?strong 能够?面试
4:block 为何可以捕获外界变量? __block 作了什么事?算法
研究Block的捕获外部变量就要除去函数参数这一项,下面一一根据这4种变量类型的捕获状况进行分析。编程
首先 全局变量global_i 和 静态全局变量static_global_j 的值增长,以及它们被 Block 捕获进去,这一点很好理解,由于是全局的,做用域很广,因此 Block 捕获了它们进去以后,在 Block 里面进行 ++ 操做, Block 结束以后,它们的值依旧能够得以保存下来。设计模式
struct __main_block_impl_0 {
struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_a_0 *a; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
__main_block_impl_0结构体 就是这样把自动变量捕获进来的。也就是说,在执行 Block 语法的时候, Block 语法表达式所使用的自动变量的值是被保存进了 Block 的结构体实例中,也就是 Block 自身中。数组
这里值得说明的一点是,若是 Block 外面还有不少自动变量,静态变量,等等,这些变量在 Block 里面并不会被使用到。那么这些变量并不会被 Block 捕获进来,也就是说并不会在构造函数里面传入它们的值。
`Block`捕获外部变量仅仅只捕获`Block`闭包里面会用到的值,其余用不到的值,它并不会去捕获。
5:谈谈你对事件的传递链和响应链的理解
回到响应链,响应链是由 UIResponser 组成的,那么是按照哪一种规则造成的。
咱们使用一个现实场景来解释这个问题:当一个用点击屏幕上的一个按钮,这个过程具体发生了什么。
经过两种方法来作这个事情。
// 先判断点是否在View内部,而后遍历subViews
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
//判断点是否在这个View内部
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds
6:谈谈 KVC 以及 KVO 的理解?
7:RunLoop 的做用是什么?它的内部工做机制了解么?
字面意思是“消息循环、运行循环”,runloop 内部实际上就是一个 do-while循环 ,它在循环监听着各类事件源、消息,对他们进行管理并分发给线程来执行。
8:苹果是如何实现 autoreleasepool 的?
arc下编译器会优化成
void *context = objc_autoreleasePoolPush();
// {}中的代码
objc_autoreleasePoolPop(context);
9:谈谈你对 FRP (函数响应式) 的理解,延伸一下 RxSwift 或者 RAC !
[参考文章:RxSwift(1)— 初探] 看这一篇文章也就够了!而后结合 RxSwift 映射到 RAC !函数响应式的思想是不变的!至于内部的封装有所不一样,可是最终倒是异曲同工!
10:平时开发有没有玩过 Instrument ?
分析:这里的内容很是有意思,对于一个iOS高级开发人员,我以为还有颇有必要掌握的!尤为开发3-5年,若是没有掌握这些内容我以为是不合格的
我我的建议在掌握面试题的同时还须要求职者更多的去分析和拓展!好比你的探索思路,你在这个知识点意外的延伸。还有你再实际开发过程的落地!而这些都是加分项!
1:什么是 isa,isa 的做用是什么?
2:一个实例对象的 isa 指向什么?类对象指向什么?元类 isa 指向什么?
类方法:
实例方法:
4: load 和 initialize 的区别?
+load
+initialize
5: _objc_msgForward 函数是作什么的?直接调用会发生什么问题?
当对象没有实现某个方法 ,会调用这个函数进行方法转发。 (某方法对应的`IMP`没找到,会返回这个函数的`IMP`去执行)
若是直接调用这个方法,就算实现了想调用的方法,也不会被调用,会直接走消息转发步骤。
6:简述下 `Objective-C` 中调用方法的过程
PS: Runtime 铸就了 Objective-C 是动态语言的特性,使得C语言具有了面向对象的特性,在程序运行期建立,检查,修改类、对象及其对应的方法,这些操做均可以使用runtime中的对应方法实现。
7:可否想向编译后获得的类中增长实例变量?可否向运行时建立的类中添加实例变量?为何?
解释:
8:谈谈你对切面编程的理解
维基百科对于切面编程(AOP)的解释是这样的:面向切面的程序设计( aspect-oriented programming,AOP,又译做面向侧面的程序设计、观点导向编程、剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为切面的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类、函数)中的横切关注点。[参考文章]
分析:Runtime 这个模块iOS面试不管初中高都会面试。我以为这个模块不光只是仅仅问问关于知识点内容,我更新想要听到求职者在这里面的爬坑探索辛历路程!Runtime 这个模块是刷开页面开发的关键点!
1:HTTP的缺陷是什么?
HTTP 主要有这些不足,例举以下。
这些问题不只在 HTTP上出现,其余未加密的协议中也会存在这类问题。
2:谈谈三次握手,四次挥手!为何是三次握手,四次挥手?
[参考文章] 我以为这个地方仍是须要自我理解,用本身的话去表达出来!
3: socket 链接和 Http 链接的区别
http 是基于 socket 之上的。socket 是一套完整的 tcp,udp 协议的接口。
TCP/IP是传输层协议,主要解决数据如何在网络中传输,而HTTP协议是应用层协议,主要解决如何包装数据。
Socket是对TCP/IP 协议的封装,它自己不是协议,而是一个调用接口,经过 Socket ,咱们才能使用 TCP/IP协议 。
http 是客户端用 http 协议进行请求,发送请求时候须要封装 http 请求头,并绑定请求的数据,服务器通常有 web 服务器配合。 http 请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开链接以节省资源。服务器不能主动给客户端响应。 iPhone 主要使用的类是 NSUrlConnection 。 socket 是客户端跟服务器直接使用 socket“套接字” 进行拼接,并无规定链接后断开,因此客户端和服务器能够保持链接,双方均可以主动发送数据。通常在游戏开发或者股票开发这种即时性很强的而且保持发送数据量比较大的场合使用。主要类是 CFSocketRef。
4:HTTPS,安全层除了SSL还有,最新的? 参数握手时首先客户端要发什么额外参数
5:何时POP网络,有了 `Alamofire` 封装网络 `URLSession`为何还要用`Moya` ?
POP网络:面向协议编程的网络可以大大下降耦合度!网络层下沉,业务层上浮。中间利用 POP网络 的 Moya 隔开。若是你的项目是 RxSwift 函数响应式的也没有关系!由于有 RxMoya
参考文章:
6:如何实现 dispatch_once
+ (instancetype)sharedInstance
{
/*定义相应类实例的静态变量;
意义:函数内定义静态变量,不管该函数被调用多少次,
在内存中只初始化一次,而且能保存最后一次赋的值
*/
static ClassName *instance = nil; /*定义一个dispatch_once_t(其实也就是整型)静态变量, 意义:做为标识下面dispatch_once的block是否已执行过。 static修饰会默认将其初始化为0,当值为0时才会执行block。 当block执行完成,底层会将onceToken设置为1,这也就是为什 么要传onceToken的地址(static修饰的变量能够经过地址修改 onceToken的值),同时底层会加锁来保证这个方法是线程安全的 */ static dispatch_once_t onceToken; /*只要当onceToken == 0时才会执行block,不然直接返回静态变量instance*/ dispatch_once(&onceToken, ^{ instance = [[ClassName alloc] init]; //... }); return instance; }
[iOS原理之CGD-dispatch_once的底层实现]
7:可否写一个读写锁?谈谈具体的分析
8:何时会出现死锁?如何避免?
9:有哪几种锁?各自的原理?它们之间的区别是什么?最好能够结合使用场景来讲
分析:这个模块多是通常开发人员的盲区。对于这一块必定要有本身的理解!学习的方向就是查漏补缺,一步一个吃掉!若是你一整块去啃,你会发现很枯燥!虽然开发过程当中你可能用不到,可是面试这一块是你必需要掌握的!
1.数据结构的存储通常经常使用的有几种?各有什么特色?
数据的存储结构是数据结构的一个重要内容。在计算机中,数据的存储结构能够采起以下四中方法来表现。
索引存储方式
散列存储方式是根据结点的关键字直接计算出该结点的存储地址的一种存储的方式。 在实际应用中,每每须要根据具体数据结构来决定采用哪种存储方式。同一逻辑结构采用不一样额存储方法,能够获得不一样的存储结构。并且这四种节本存储方法,既能够单独使用,也能够组合起来对数据结构进行存储描述。
2.集合结构 线性结构 树形结构 图形结构 3.单向链表 双向链表 循环链表 4.数组和链表区别 5.堆、栈和队列
- [iOS经常使用算法和数据结构]
- [数据结构初探]这里面介绍了不少数据结构,你们还能够自行查阅更多资料
6.输入一棵二叉树的根结点,求该树的深度?
若是一棵树只有一个结点,它的深度为1。 若是根结点只有左子树而没有右子树, 那么树的深度应该是其左子树的深度加1,一样若是根结点只有右子树而没有左子树,那么树的深度应该是其右子树的深度加1\. 若是既有右子树又有左子树, 那该树的深度就是其左、右子树深度的较大值再加1。
public static int treeDepth(BinaryTreeNode root) {
if (root == null) { return 0; } int left = treeDepth(root.left); int right = treeDepth(root.right); return left > right ? (left + 1) : (right + 1); }
7.输入一课二叉树的根结点,判断该树是否是平衡二叉树?
1.时间复杂度
在[计算机科学]中,时间复杂性,又称时间复杂度,[算法],它定性描述该算法的运行时间。这是一个表明算法输入值的[字符串]的长度的函数。时间复杂度经常使用[大O符号]表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是[渐近]的,亦即考察输入值大小趋近无穷时的状况。 [时间复杂性]
2.空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程当中临时占用存储空间大小的量度,记作S(n)=O(f(n))。好比直接[插入排序]的[时间复杂度],空间复杂度是O(1) 。而通常的[递归]算法就要有O(n)的空间复杂度了,由于每次递归都要存储返回信息。一个算法的优劣主要从算法的执行时间和所须要占用的存储空间两个方面[衡量]。 [时间复杂度&空间复杂度]
3.经常使用的排序算法
4.字符串反转
- (NSString *)reversalString:(NSString *)originString{
NSString *resultStr = @""; for (NSInteger i = originString.length -1; i >= 0; i--) { NSString *indexStr = [originString substringWithRange:NSMakeRange(i, 1)]; resultStr = [resultStr stringByAppendingString:indexStr]; } return resultStr; }
5.链表反转(头差法)
public Node reverseList(){
Node cur = head; Node prev = null; Node curNext = head.next; Node reverHead = null; while(cur!=null){ cur.next = prev; cur = curNext; prev = cur; curNext = curNext.next; } reverHead = cur; return reverHead; }
6.有序数组合并
objc
- (void)merge { /* 有序数组A:一、四、五、八、10...1000000,有序数组B:二、三、六、七、9...999998,A、B两个数组不相互重复,请合并成一个有序数组C,写出代码和时间复杂度。 */ //(1). NSMutableArray *A = [NSMutableArray arrayWithObjects:@4,@5,@8,@10,@15, nil]; // NSMutableArray *B = [NSMutableArray arrayWithObjects:@2,@6,@7,@9,@11,@17,@18, nil]; NSMutableArray *B = [NSMutableArray arrayWithObjects:@2,@6,@7,@9,@11,@12,@13, nil]; NSMutableArray *C = [NSMutableArray array]; int count = (int)A.count+(int)B.count; int index = 0; for (int i = 0; i < count; i++) { if (A[0]<B[0]) { [C addObject:A[0]]; [A removeObject:A[0]]; } else if (B[0]<A[0]) { [C addObject:B[0]]; [B removeObject:B[0]]; } if (A.count==0) { [C addObjectsFromArray:B]; NSLog(@"C = %@",C); index = i+1; NSLog(@"index = %d",index); return; } else if (B.count==0) { [C addObjectsFromArray:A]; NSLog(@"C = %@",C); index = i+1; NSLog(@"index = %d",index); return; } } //(2). //时间复杂度 //T(n) = O(f(n)):用"T(n)"表示,"O"为数学符号,f(n)为同数量级,通常是算法中频度最大的语句频度。 //时间复杂度:T(n) = O(index); }
7.查找第一个只出现一次的字符(Hash查找)
两个思路:
# define SIZE 256
char GetChar(char str[])
{
if(!str) return 0; char* p = NULL; unsigned count[SIZE] = {0}; char buffer[SIZE]; char* q = buffer; for(p=str; *p!=0; p++) { if(++count[(unsigned char)*p] == 1) *q++ = *p; } for (p=buffer; p<q; p++) { if(count[(unsigned char)*p] == 1) return *p; } return 0; }
8.查找两个子视图的共同父视图
这个问的实际上是数据结构中的二叉树,查找一个普通二叉树中两个节点最近的公共祖先问题 假设两个视图为 UIViewA 、 UIViewC ,其中 UIViewA 继承于 UIViewB , UIViewB 继承于 UIViewD , UIViewC 也继承于 UIViewD ;即 A->B->D,C->D
- (void)viewDidLoad {
[super viewDidLoad];
Class commonClass1 = [self commonClass1:[ViewA class] andClass:[ViewC class]]; NSLog(@"%@",commonClass1); // 输出:2018-03-22 17:36:01.868966+0800 两个UIView的最近公共父类[84288:2458900] ViewD } // 获取全部父类 - (NSArray *)superClasses:(Class)class { if (class == nil) { return @[]; } NSMutableArray *result = [NSMutableArray array]; while (class != nil) { [result addObject:class]; class = [class superclass]; } return [result copy]; } - (Class)commonClass1:(Class)classA andClass:(Class)classB { NSArray *arr1 = [self superClasses:classA]; NSArray *arr2 = [self superClasses:classB]; for (NSUInteger i = 0; i < arr1.count; ++i) { Class targetClass = arr1[i]; for (NSUInteger j = 0; j < arr2.count; ++j) { if (targetClass == arr2[j]) { return targetClass; } } } return nil; }
- (Class)commonClass2:(Class)classA andClass:(Class)classB{
NSArray *arr1 = [self superClasses:classA]; NSArray *arr2 = [self superClasses:classB]; NSSet *set = [NSSet setWithArray:arr2]; for (NSUInteger i =0; i<arr1.count; ++i) { Class targetClass = arr1[i]; if ([set containsObject:targetClass]) { return targetClass; } } return nil; }
9.无序数组中的中位数(快排思想)
10.给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你能够假设每一个输入只对应一种答案,且一样的元素不能被重复利用。 示例:给定 nums = [2, 7, 11, 15], target = 9 --- 返回 [0, 1] 思路:
class Solution {
public int[] twoSum(int[] nums, int target) { int len = nums.length; int[] result = new int[2]; for(int i = 0; i < len; i++){ for(int j = i+1; j < len; j++){ if(nums[i] + nums[j] == target){ result[0] = i; result[1] = j; return result; } } } return result; } }
分析:这个模块是绝大部分开发人员的软肋!这个模块是最能测试求职者思惟能力的!可是我不建议面试官直接让求职者手写 在那样的面试紧张环境,手写数据结构或者一些算法代码,是很是有挑战的!思惟到我以为差很少!
1:设计模式是为了解决什么问题的?
设计模式(Design pattern)是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式最主要解决的问题是经过封装和隔离变化点来处理软件的各类变化问题。 隔离变化的好处在于,将系统中常常变化的部分和稳定的部分隔离,有助于增长复用性,并下降系统耦合度。不少设计模式的意图中都明显地指出了其对问题的解决方案,学习设计模式的要点是发现其解决方案中封装的变化点。
三本经典书籍:[《GOF设计模式》],[《设计模式解析》],《Head First Design Pattern》
设计模式是软件开发领域的精髓之一。学好设计模式是目前每个开发人员的必修课,
2:看过哪些第三方框架的源码,它们是怎么设计的?
这个题目就看你我的的感触,考量你平时的功底! 你们能够针对性一些常见的框架: RxSwift 、 Alamofire 、 Moya 、 AFNetworing 、 YYKit .... 掌握会用的同时,必需要掌握底层的核心思想!
3:能够说几个重构的技巧么?你以为重构适合何时来作?
在新功能增长时候,在扩展再也不简单的时候。重构是一个不断的过程。
4:开发中经常使用架构设计模式你怎么选型?
这里也是一道开放性题目!并非说某一种架构就是最优秀的~只有最合适的!根据公司状况,项目现状,以及开发者水平及时调整,设计!
5:你是如何组件化解耦的?
iOS 解藕 、组件化最经常使用的是使用统跳路由的方式,目前比较经常使用的 iOS 开源路由框架主要是 JLRoutes 、 MGJRouter 、 HHRouter 等,这些路由框架各有优势和缺点,基本能够知足大部分需求。目前最经常使用来做路由跳转,以实现基本的组件化开发,实现各模块之间的解藕。可是,在实际中开发中会发现,没法完全使用它们完成全部模块间通讯,好比模块间的同步、异步通讯等。再好比,咱们在配置了相关路由跳转的 URL 后,如何在上线以后动态修改相关跳转逻辑?在模块间通讯时,如何在上线后动态修改相关参数?APP 可否实现相似 Web 的302跳转 ?[学习参考]
分析:架构设计这一层对于一个iOS中高级开发人员来讲。这一块那是他必需要去思考和感觉总结的!若是这位求职者开发4-5年了,一直都在作应用层界面开发,那么想必他将来的职业晋升是已经落后了的!面试官不妨在这一个模块单独设计成一面,就和求职者一块儿交流讨论。毕竟这些思惟的设计,也许可以给面试官带来一些不同的东西!😊
1:`tableView` 有什么好的性能优化方案?
2: 界面卡顿和检测你都是怎么处理?
3:谈谈你对离屏渲染的理解?
4:如何下降APP包的大小
5:平常如何检查内存泄露?
6:APP启动时间应从哪些方面优化?
分析:如今APP性能优化以及成为iOS中高级开发人员必需要去关系的东西!这一块我我的建议结合实际开发去和求职者交流。而不是仅仅停留在知识点问答,由于没有实际开发能力的性能优化都只是纸上谈兵!
这一套面试题仍是有必定的水平和难度的!可是对于要应聘一份iOS中高级开发岗位,仍是比较中肯的!但愿你们可以在接下来的跳槽涨薪有本身的思想。
文章有长,建议关注备份,无论是正在面试仍是即将面试,应该对你有帮助,既然看到这里:麻烦点个赞吧!👍
PS:对本文内容存在疑问还望指出,谢谢!加油,静候你的佳音
多关注我,后续会慢慢分享独立开发者心得和干货(扫码加我,拉你进微信群)
做者:Cooci连接:https://juejin.im/post/5d8e150d518825097013297d