iOS底层面试题(下篇)

沉舟侧畔千帆进,病树前头万木春。这篇文章主要讲述iOS底层面试题(下篇)相关的知识,希望能为你提供帮助。
【iOS底层面试题(下篇)】

iOS底层面试题(下篇)

文章图片

流程:
  • 首先在手机上安装Charles证书
  • 在代理设置中开启Enable SSL Proxying
  • 之后添加需要抓取服务端的地址
原理:
Charles作为中间人,对客户端伪装成服务端,对服务端伪装成客户端。简单来说:
  • 截获客户端的HTTPS请求,伪装成中间人客户端去向服务端发送HTTPS请求
  • 接受服务端返回,用自己的证书伪装成中间人服务端向客户端发送数据内容。
具体流程如下图:扯一扯HTTPS单向认证、双向认证、抓包原理、反抓包策略
iOS底层面试题(下篇)

文章图片

14. 什么是中间人?如何避免?中间人就是截获到客户端的请求以及服务器的响应,比如Charles抓取HTTPS的包就属于中间人。
避免的方式:客户端可以预埋证书在本地,然后进行证书的比较是否是匹配的
15. 了解编译的过程么?分为哪几个步骤?1:预编译:主要处理以“#”开始的预编译指令。
2:编译:
3:汇编:汇编器将汇编代码转变成机器指令。
最后,进程的控制权转交给程序入口,程序终于运行起来了。
16. 静态链接了解么?静态库和动态库的区别?静态链接是指将多个目标文件合并为一个可执行文件,直观感觉就是将所有目标文件的段合并。需要注意的是可执行文件与目标文件的结构基本一致,不同的是是否“可执行”。
17. App网络层有哪些优化策略?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的区别:
在调用[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 Class objc_object::getIsa() { if (isTaggedPointer()) { uintptr_t slot = ((uintptr_t)this > > TAG_SLOT_SHIFT) & TAG_SLOT_MASK; return objc_tag_classes[slot]; } return ISA(); }inline Class objc_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-& gt; getIsa(),最后在ISA()方法中获得meta class的指针。
  • 接着在isKindOfClass中有一个循环,先判断class是否等于meta class,不等就继续循环判断是否等于super class,不等再继续取super class,如此循环下去。
  • [NSObject class]执行完之后调用isKindOfClass,第一次判断先判断NSObjectNSObjectmeta class是否相等,之前讲到meta class的时候放了一张很详细的图,从图上我们也可以看出,NSObjectmeta class与本身不等。
  • 接着第二次循环判断NSObjectmeta classsuperclass是否相等。还是从那张图上面我们可以看到:Root class(meta)superclass 就是 Root class(class),也就是NSObject本身。所以第二次循环相等,于是第一行res1输出应该为YES
  • 同理,[Sark class]执行完之后调用isKindOfClass,第一次for循环,SarkMeta Class[Sark class]不等,第二次for循环Sark Meta Class的super class 指向的是 NSObject Meta Class, 和 Sark Class不相等。
  • 第三次for循环,NSObject Meta Classsuper class指向的是NSObject Class,和 Sark Class 不相等。第四次循环,NSObject Classsuper class 指向 nil, 和 Sark Class不相等。第四次循环之后,退出循环,所以第三行的res3输出为NO
  • 如果把这里的Sark改成它的实例对象,[sark isKindOfClass:[Sark class],那么此时就应该输出YES了。因为在isKindOfClass函数中,判断sark的meta class是自己的元类Sark,第一次for循环就能输出YES了。
  • isMemberOfClass的源码实现是拿到自己的isa指针和自己比较,是否相等。
  • 第二行isa 指向 NSObjectMeta Class,所以和 NSObject Class不相等。第四行,isa指向SarkMeta 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

这道题有两个难点。
首先需要谈谈隐藏参数self和_cmd的问题。 当[receiver message]调用方法时,系统会在运行时偷偷地动态传入两个隐藏参数self_cmd,之所以称它们为隐藏参数,是因为在源代码中没有声明和定义这两个参数。self在已经明白了,接下来就来说说_cmd_cmd表示当前调用方法,其实它就是一个方法选择器SEL
id cls = [Sark class]; void *obj = & cls;

答案是可以的。obj被转换成了一个指向Sark Class的指针,然后使用id转换成了objc_object类型。obj现在已经是一个Sark类型的实例对象了。当然接下来可以调用speak的方法。
很多人可能会认为会输出sark相关的信息。这样答案就错误了。
正确的答案会输出
my name is & lt; ViewController: 0x7ff6d9f31c50& gt;
内存地址每次运行都不同,但是前面一定是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> , 地址 = 0x7fff543f5aa8 Sark class = Sark 地址 = 0x7fff543f5a88 Void *obj = < Sark: 0x7fff543f5a88> 地址 = 0x7fff543f5a80my name is < ViewController: 0x7fb570e2ad00> Sark instance = < Sark: 0x7fb570d20b10> 地址 = 0x7fff543f5a78 my 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
所以输出为my name is **& lt; ViewController: 0x7fb570e2ad00& gt; **
至此,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> , 地址 = 0x7fff56a48a78 Sark class = Sark 地址 = 0x7fff56a48a50 Void *obj = < Sark: 0x7fff56a48a50> 地址 = 0x7fff56a48a48my name is halfrostSark instance = < Sark: 0x6080000233e0> 地址 = 0x7fff56a48a40 my name is (null)

由于加了一个字符串,结果输出就完全变了,[(__bridge id)obj speak]; 这句话会输出“my name is halfrost”
原因还是和上面的类似。按viewDidLoad执行时各个变量入栈顺序从高到底为self_cmdself.classselfmyNameobjobj往上偏移32位,就是myName字符串,所以输出变成了输出myName了。
20. 排序题:冒泡排序,选择排序,插入排序,快速排序(二路,三路)能写出那些?
  • 冒泡排序
extension Array where Element : Comparable{ public mutating func bubbleSort() { let count = self.count for 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.count for i in 0..< count { var minIndex = i for 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.count guard count > 1 else { return } for i in 1..< count { var preIndex = i - 1 let currentValue = https://www.songbingjia.com/android/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 = left let 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 = left let 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 = right let 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 += 1 r -= 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

    推荐阅读