沉舟侧畔千帆进,病树前头万木春。这篇文章主要讲述iOS底层面试题(下篇)相关的知识,希望能为你提供帮助。
【iOS底层面试题(下篇)】
文章图片
流程:
- 首先在手机上安装Charles证书
- 在代理设置中开启Enable SSL Proxying
- 之后添加需要抓取服务端的地址
Charles
作为中间人,对客户端伪装成服务端,对服务端伪装成客户端。简单来说:- 截获客户端的HTTPS请求,伪装成中间人客户端去向服务端发送HTTPS请求
- 接受服务端返回,用自己的证书伪装成中间人服务端向客户端发送数据内容。
文章图片
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
,第一次判断先判断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。
**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
,_cmd
,self.class
,self
,myName
,obj
。obj
往上偏移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
推荐阅读
- UNITY3D 游戏开发之八Unity编译到iPhone运行 Collider 无法正常触发事件解决方案
- 开发者如何快速搭建自己的电商App?
- iOS开发面试只需知道这些,技术基本通关!(Runtime篇)
- 手语也能机器翻译了!机器学习手势识别功能了解一下
- NA嵌入Flutter页面
- 由美团文章“一款可以让大型iOS工程编译速度提升50%的工具”引出的.hmap文件探索(上)
- Flutter Utils 全网最齐全的工具类
- Android技术分享| 自定义ViewGroup实现直播间大小屏无缝切换
- 如何正确跟踪广告转化数据,优化广告投放效果()