7月,iOS求职跳槽的绝对较少,能在这个时间段求职的,不是被迫,就是对本人的技术很自信;
针对7月,特地总结了第三份iOS常见大厂面试题(下);iOS面试题分为 上、中、下三局部,不便大家观看;
请先本人
答一答
!
话不多说;间接上题
本文收录:公众号【iOS进阶宝典《iOS底层面试题(下篇)》】
13. 如何用Charles抓HTTPS的包?其中原理和流程是什么?
流程:
- 首先在手机上安装Charles证书
- 在代理设置中开启Enable SSL Proxying
- 之后增加须要抓取服务端的地址
原理:
Charles
作为中间人,对客户端伪装成服务端,对服务端伪装成客户端。简略来说:
- 截获客户端的HTTPS申请,伪装成中间人客户端去向服务端发送HTTPS申请
- 承受服务端返回,用本人的证书伪装成中间人服务端向客户端发送数据内容。
具体流程如下图:扯一扯HTTPS单向认证、双向认证、抓包原理、反抓包策略
14. 什么是中间人攻打?如何防止?
中间人攻打就是截获到客户端的申请以及服务器的响应,比方Charles
抓取HTTPS的包就属于中间人攻打。
防止的形式:客户端能够预埋证书在本地,而后进行证书的比拟是否是匹配的
15. 理解编译的过程么?分为哪几个步骤?
1:预编译:次要解决以“#”开始的预编译指令。
2:编译:
- 词法剖析:将字符序列宰割成一系列的记号。
- 语法分析:依据产生的记号进行语法分析生成语法树。
- 语义剖析:剖析语法树的语义,进行类型的匹配、转换、标识等。
- 两头代码生成:源码级优化器将语法树转换成中间代码,而后进行源码级优化,比方把 1+2 优化为 3。中间代码使得编译器被分为前端和后端,不同的平台能够利用不同的编译器后端将中间代码转换为机器代码,实现跨平台。
- 指标代码生成:尔后的过程属于编译器后端,代码生成器将中间代码转换成指标代码(汇编代码),其后指标代码优化器对指标代码进行优化,比方调整寻址形式、应用位移代替乘法、删除多余指令、调整指令程序等。
3:汇编:汇编器将汇编代码转变成机器指令。
- 动态链接:链接器将各个曾经编译成机器指令的指标文件链接起来,通过重定位过后输入一个可执行文件。
- 装载:装载可执行文件、装载其依赖的共享对象。
- 动静链接:动静链接器将可执行文件和共享对象中须要重定位的地位进行修改。
最初,过程的控制权转交给程序入口,程序终于运行起来了。
16. 动态链接理解么?动态库和动静库的区别?
动态链接是指将多个指标文件合并为一个可执行文件,直观感觉就是将所有指标文件的段合并。须要留神的是可执行文件与指标文件的构造基本一致,不同的是是否“可执行”。
- 动态库:链接时残缺地拷贝至可执行文件中,被屡次应用就有多份冗余拷贝。
- 动静库:链接时不复制,程序运行时由零碎动静加载到内存,供程序调用,零碎只加载一次,多个程序共用,节俭内存。
17. App网络层有哪些优化策略?
- 优化DNS解析和缓存
- 对传输的数据进行压缩,缩小传输的数据
- 应用缓存伎俩缩小申请的发动次数
- 应用策略来缩小申请的发动次数,比方在上一个申请未着地之前,不进行新的申请
- 防止网络抖动,提供重发机制
18:[self class] 与 [super class]
@implementation Son : Father(id)init { self = [super init]; if (self) { NSLog(@"%@", NSStringFromClass([self class])); NSLog(@"%@", NSStringFromClass([super class])); } return self; } @end
self和super的区别:
self
是类的一个暗藏参数,每个办法的实现的第一个参数即为self
。- super并不是暗藏参数,它实际上只是一个”编译器标示符”,它负责通知编译器,当调用办法时,去调用父类的办法,而不是本类中的办法。
在调用[super class]
的时候,runtime
会去调用objc_msgSendSuper
办法,而不是objc_msgSend
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )/// Specifies the superclass of an instance.struct objc_super {/// Specifies an instance of a class.__unsafe_unretained id receiver;/// Specifies the particular superclass of the instance to message. # if !defined(__cplusplus) && !__OBJC2__/* For compatibility with old objc-runtime.h header */__unsafe_unretained Class class;# else__unsafe_unretained Class super_class;# endif/* super_class is the first class to search */}
在objc_msgSendSuper
办法中,第一个参数是一个objc_super
的构造体,这个构造体外面有两个变量,一个是接管音讯的receiver
,一个是以后类的父类super_class
。
从objc_super
构造体指向的superClass
父类的办法列表开始查找selector,父类找到了,父类就执行这个办法。
class 办法的外部实现:
- (Class)class {return object_getClass(self);}
在class 办法内,默认传入的是self, 无论调用者是谁。
所以这个道题的答案就进去了: 两个打印的都是以后的类。
18.isKindOfClass 与 isMemberOfClass
上面代码输入什么?
@interface Sark : NSObject@end@implementation Sark@endint main(int argc, const char * argv[]) {@autoreleasepool {BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];NSLog(@"%d %d %d %d", res1, res2, res3, res4);}return 0;}
先来剖析一下源码这两个函数的对象实现
+ (Class)class {return self;}(Class)class { return object_getClass(self); }Class object_getClass(id obj){if (obj) return obj->getIsa();else return Nil;}inline Classobjc_object::getIsa(){if (isTaggedPointer()) {uintptr_t slot = ((uintptr_t)this >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;return objc_tag_classes[slot];}return ISA();}inline Classobjc_object::ISA(){assert(!isTaggedPointer());return (Class)(isa.bits & ISA_MASK);}(BOOL)isKindOfClass:(Class)cls { for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; } (BOOL)isKindOfClass:(Class)cls { for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; }(BOOL)isMemberOfClass:(Class)cls { return object_getClass((id)self) == cls; }(BOOL)isMemberOfClass:(Class)cls { return [self class] == cls; }
首先题目中NSObject 和 Sark别离调用了class办法。
+ (BOOL)isKindOfClass:(Class)cls
办法外部,会先去取得object_getClass
的类,而object_getClass
的源码实现是去调用以后类的obj->getIsa()
,最初在ISA()
办法中取得meta class
的指针。- 接着在
isKindOfClass
中有一个循环,先判断class
是否等于meta class
,不等就持续循环判断是否等于super class
,不等再持续取super class
,如此循环上来。
[NSObject class]
执行完之后调用isKindOfClass
,第一次判断先判断NSObject
和NSObject
的meta class
是否相等,之前讲到meta class
的时候放了一张很具体的图,从图上咱们也能够看出,NSObject
的meta class
与自身不等。- 接着第二次循环判断
NSObject
与meta class
的superclass
是否相等。还是从那张图下面咱们能够看到:Root class(meta)
的superclass
就是Root class(class)
,也就是NSObject
自身。所以第二次循环相等,于是第一行res1
输入应该为YES
。 - 同理,
[Sark class]
执行完之后调用isKindOfClass
,第一次for
循环,Sark
的Meta Class
与[Sark class]
不等,第二次for循环
,Sark
Meta Class的super class
指向的是NSObject Meta Class
, 和Sark Class
不相等。 - 第三次for循环,
NSObject Meta Class
的super class
指向的是NSObject Class
,和Sark Class
不相等。第四次循环,NSObject Class
的super class
指向nil
, 和Sark Class
不相等。第四次循环之后,退出循环,所以第三行的res3输入为NO
。 - 如果把这里的Sark改成它的实例对象,
[sark isKindOfClass:[Sark class]
,那么此时就应该输入YES
了。因为在isKindOfClass
函数中,判断sark的meta class
是本人的元类Sark
,第一次for循环就能输入YES
了。 isMemberOfClass
的源码实现是拿到本人的isa指针
和本人比拟,是否相等。- 第二行
isa
指向NSObject
的Meta Class
,所以和NSObject Class
不相等。第四行,isa
指向Sark
的Meta Class
,和Sark Class
也不等,所以第二行res2
和第四行res4
都输入NO。
19.Class与内存地址
上面的代码会?**Compile Error / Runtime Crash / NSLog…?**
@interface Sark : NSObject@property (nonatomic, copy) NSString *name;(void)speak; @end @implementation Sark(void)speak { NSLog(@"my name's %@", [self.name](http://self.name/)); } @end @implementation ViewController(void)viewDidLoad { [super viewDidLoad]; id cls = [Sark class]; void *obj = &cls; [(__bridge id)obj speak]; } @end
这道题有两个难点。
- 难点一:
obj
调用speak
办法,到底会不会解体。- 难点二:如果
speak
办法不解体,应该输入什么?
首先须要谈谈暗藏参数self和_cmd的问题。 当[receiver message]
调用办法时,零碎会在运行时偷偷地动静传入两个暗藏参数self
和_cmd
,之所以称它们为暗藏参数,是因为在源代码中没有申明和定义这两个参数。self
在曾经明确了,接下来就来说说_cmd
。_cmd
示意以后调用办法,其实它就是一个办法选择器SEL
。
难点一,能不能调用speak
办法?
id cls = [Sark class];void *obj = &cls;
答案是能够的。obj
被转换成了一个指向Sark Class
的指针,而后应用id
转换成了objc_object
类型。obj
当初曾经是一个Sark
类型的实例对象了。当然接下来能够调用speak的办法。
难点二,如果能调用speak
,会输入什么呢?
很多人可能会认为会输入sark相干的信息。这样答案就谬误了。
正确的答案会输入
my name is <ViewController: 0x7ff6d9f31c50>
内存地址每次运行都不同,然而后面肯定是ViewController。why?
咱们把代码扭转一下,打印更多的信息进去。
- (void)viewDidLoad {[super viewDidLoad];NSLog(@"ViewController = %@ , 地址 = %p", self, &self);id cls = [Sark class];NSLog(@"Sark class = %@ 地址 = %p", cls, &cls);void *obj = &cls;NSLog(@"Void *obj = %@ 地址 = %p", obj,&obj);[(__bridge id)obj speak];Sark *sark = [[Sark alloc]init];NSLog(@"Sark instance = %@ 地址 = %p",sark,&sark);[sark speak];}
咱们把对象的指针地址都打印进去。输入后果:
ViewController = <ViewController: 0x7fb570e2ad00> , 地址 = 0x7fff543f5aa8Sark class = Sark 地址 = 0x7fff543f5a88Void *obj = <Sark: 0x7fff543f5a88> 地址 = 0x7fff543f5a80my name is <ViewController: 0x7fb570e2ad00>Sark instance = <Sark: 0x7fb570d20b10> 地址 = 0x7fff543f5a78my name is (null)
objc_msgSendSuper2 解读
// objc_msgSendSuper2() takes the current search class, not its superclass.OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
objc_msgSendSuper2办法入参是一个objc_super *super。
/// Specifies the superclass of an instance.struct objc_super {/// Specifies an instance of a class.__unsafe_unretained id receiver;/// Specifies the particular superclass of the instance to message. # if !defined(__cplusplus) && !**OBJC2**/* For compatibility with old objc-runtime.h header */__unsafe_unretained Class class;# else__unsafe_unretained Class super_class;# endi/* super_class is the first class to search */};# endif
所以按viewDidLoad执行时各个变量入栈程序从高到底为self
, _cmd
, self.class
, self
, obj
。
- 第一个
self
和第二个_cmd
是暗藏参数。- 第三个
self.class
和第四个self
是[super viewDidLoad]
办法执行时候的参数。- 在调用
self.name
的时候,实质上是self
指针在内存向高位地址偏移一个指针。在32位上面,一个指针是4字节=4*8bit=32bit
。(64位不一样然而思路是一样的)- 从打印后果咱们能够看到,
obj
就是cls
的地址。在obj
向上偏移32bit
就到了0x7fff543f5aa8
,这正好是ViewController
的地址。
所以输入为my name is **<ViewController: 0x7fb570e2ad00>**
。
至此,Objc
中的对象到底是什么呢?
本质:Objc
中的对象是一个指向ClassObject
地址的变量,即 id obj = &ClassObject
, 而对象的实例变量 void *ivar = &obj + offset(N)
加深一下对下面这句话的了解,上面这段代码会输入什么?
- (void)viewDidLoad {[super viewDidLoad];NSLog(@"ViewController = %@ , 地址 = %p", self, &self);NSString *myName = @"halfrost";id cls = [Sark class];NSLog(@"Sark class = %@ 地址 = %p", cls, &cls);void *obj = &cls;NSLog(@"Void *obj = %@ 地址 = %p", obj,&obj);[(__bridge id)obj speak];Sark *sark = [[Sark alloc]init];NSLog(@"Sark instance = %@ 地址 = %p",sark,&sark);[sark speak];}ViewController = <ViewController: 0x7fff44404ab0> , 地址 = 0x7fff56a48a78Sark class = Sark 地址 = 0x7fff56a48a50Void *obj = <Sark: 0x7fff56a48a50> 地址 = 0x7fff56a48a48my name is halfrostSark instance = <Sark: 0x6080000233e0> 地址 = 0x7fff56a48a40my name is (null)
因为加了一个字符串,后果输入就齐全变了,[(__bridge id)obj speak]
;这句话会输入“my name is halfrost”
起因还是和下面的相似。按viewDidLoad
执行时各个变量入栈程序从高到底为self
,_cmd
,self.class
,self
,myName
,obj
。obj
往上偏移32位,就是myName
字符串,所以输入变成了输入myName
了。
20. 排序题:冒泡排序,抉择排序,插入排序,疾速排序(二路,三路)能写出那些?
这里简略的说下几种疾速排序的不同之处,随机快排,是为了解决在近似有序的状况下,工夫复杂度会进化为O(n^2)
,双路快排是为了解决疾速排序在大量数据反复的状况下,工夫复杂度会进化为O(n^2)
,三路快排是在大量数据反复的状况下,对双路快排的一种优化。
- 冒泡排序
extension Array where Element : Comparable{public mutating func bubbleSort() {let count = self.countfor i in 0..<count {for j in 0..<(count - 1 - i) {if self[j] > self[j + 1] {(self[j], self[j + 1]) = (self[j + 1], self[j])}}}}}
- 抉择排序
extension Array where Element : Comparable{public mutating func selectionSort() {let count = self.countfor i in 0..<count {var minIndex = ifor j in (i+1)..<count {if self[j] < self[minIndex] {minIndex = j}}(self[i], self[minIndex]) = (self[minIndex], self[i])}}}
- 插入排序
extension Array where Element : Comparable{public mutating func insertionSort() {let count = self.countguard count > 1 else { return }for i in 1..<count {var preIndex = i - 1let currentValue = self[i]while preIndex >= 0 && currentValue < self[preIndex] {self[preIndex + 1] = self[preIndex]preIndex -= 1}self[preIndex + 1] = currentValue}}}
- 疾速排序
extension Array where Element : Comparable{public mutating func quickSort() {func quickSort(left:Int, right:Int) {guard left < right else { return }var i = left + 1,j = leftlet key = self[left]while i <= right {if self[i] < key {j += 1(self[i], self[j]) = (self[j], self[i])}i += 1}(self[left], self[j]) = (self[j], self[left])quickSort(left: j + 1, right: right)quickSort(left: left, right: j - 1)}quickSort(left: 0, right: self.count - 1)}}
- 随机快排
extension Array where Element : Comparable{public mutating func quickSort1() {func quickSort(left:Int, right:Int) {guard left < right else { return }let randomIndex = Int.random(in: left...right)(self[left], self[randomIndex]) = (self[randomIndex], self[left])var i = left + 1,j = leftlet key = self[left]while i <= right {if self[i] < key {j += 1(self[i], self[j]) = (self[j], self[i])}i += 1}(self[left], self[j]) = (self[j], self[left])quickSort(left: j + 1, right: right)quickSort(left: left, right: j - 1)}quickSort(left: 0, right: self.count - 1)}}
- 双路快排
extension Array where Element : Comparable{public mutating func quickSort2() {func quickSort(left:Int, right:Int) {guard left < right else { return }let randomIndex = Int.random(in: left...right)(self[left], self[randomIndex]) = (self[randomIndex], self[left])var l = left + 1, r = rightlet key = self[left]while true {while l <= r && self[l] < key {l += 1}while l < r && key < self[r]{r -= 1}if l > r { break }(self[l], self[r]) = (self[r], self[l])l += 1r -= 1}(self[r], self[left]) = (self[left], self[r])quickSort(left: r + 1, right: right)quickSort(left: left, right: r - 1)}quickSort(left: 0, right: self.count - 1)}}
- 三路快排
// 三路快排extension Array where Element : Comparable{ public mutating func quickSort3() { func quickSort(left:Int, right:Int) { guard left < right else { return } let randomIndex = Int.random(in: left...right) (self[left], self[randomIndex]) = (self[randomIndex], self[left]) var lt = left, gt = right var i = left + 1 let key = self[left] while i <= gt { if self[i] == key { i += 1 }else if self[i] < key{ (self[i], self[lt + 1]) = (self[lt + 1], self[i]) lt += 1 i += 1 }else { (self[i], self[gt]) = (self[gt], self[i]) gt -= 1 } } (self[left], self[lt]) = (self[lt], self[left]) quickSort(left: gt + 1, right: right) quickSort(left: left, right: lt - 1) } quickSort(left: 0, right: self.count - 1) }}
文末举荐:iOS热门文集&视频解析
① Swift
② iOS底层技术
③ iOS逆向防护
④ iOS面试合集
⑤ 大厂面试题+底层技术+逆向安防+Swift
喜爱的小伙伴记得点赞喔~
珍藏等于白嫖,点赞才是真情( ´・・` )