1.Runtime是什么?
- runtime 是由C 和C++ 汇编 实现的⼀套API,为OC语⾔加⼊了⾯向对象,运⾏时的功能
2、⽅法的本质,sel是什么?IMP是什么?两者之间的关系⼜是什么?
⽅法的本质:发送消息,消息会有以下⼏个流程
- 快速查找(objc_msgSend)~cache_t缓存消息
- 慢速查找~递归⾃⼰|⽗类~lookUpImpOrForward
- 查找不到消息:动态⽅法解析~resolveInstanceMethod
- 消息快速转发~forwardingTargetForSelector
- 消息慢速转发~methodSignatureForSelector&forwardInvocation
sel是⽅法编号~在read_images期间就编译进⼊了内存imp就是我们函数实现指针,找imp就是找函数的过程
- sel就相当于书本的⽬录tittle
- imp就是书本的⻚码
查找具体的函数就是想看这本书⾥⾯具体篇章的内容
- 我们⾸先知道想看什么~tittle(sel)
- 根据⽬录对应的⻚码(imp)
- 查找到具体的内容
3、能否向编译后的得到的类中增加实例变量?能否想运⾏时创建的类中添加实例变:
-
不能向编译后的得到的类中增加实例变量
-
只要内没有注册到内存还是可以添加
原因:我们编译好的实例变量存储的位置在ro,⼀旦编译完成,内存结构就完全确定就⽆法修改
可以添加属性+⽅法 这个是在rw区内添加
4、[selfclass]和[superclass]的区别以及原理分析
- [self class]:就是发送消息objc_msgSend,消息接受者是self⽅法编号:class
- [super class]:本质就是objc_msgSendSuper,消息的接受者还是self⽅法编号:class 只是objc_msgSendSuper会更快直接跳过self的查找
5、Runtime是如何实现weak的,为什么可以⾃动置nil
- 通过SideTable找到我们的weak_table
- weak_table根据referent找到或者创建weak_entry_t
- 然后append_referrer(entry,referrer)将我的新弱引⽤的对象加进去entry
- 最后weak_entry_insert 把entry加⼊到我们的weak_table
6、MethodSwizzling的坑与应⽤
应用:主要用于方法交换,使用自己方法的selct指向另一个函数的imp(函数实现),从而在调用系统函数时,调用到自己的方法从而实现HOOK方法的作用;
坑点:具体解决办法可以下载 MethodSwizzling坑Demo
1.一般情况methodSwizzling方法都会+(void)load方法中,使得方法能够更早的并且自动执行,但是有可能会有人手动调用load方法使得方法二次指回系统函数,
解决办法:使用单利方式在load方法中实现
2.交换方法自己没有实现,父类有实现;
解决办法:首先给自己添加需要交换的方法,然后再将父类的IMP给swizzling
3.交换方法中自己没有实现只用声明
解决办法:在交换之前可以先添加一个简单的实现,然后再进行交换
7、内存偏移问题
首先运行下面代码我们来看输出结果:
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
NSLog(@"\na = %p\nb = %p\nc = %p\nd = %p\n",&a,&b,&c,&d);
}
输出结果为:可以得到结论是,栈内存是连续的;
接下来我们来看一下下面一个代码:
{
//pcls ->XZPerson 类对象
id pcls = [XZPerson class];
void *pp= &pcls;
[(__bridge id)pp saySomething];
// p -> XZPerson 实例对象
XZPerson *p = [XZPerson alloc];
[p saySomething];
}
//XZPerson类中的实现
- (void)saySomething{
NSLog(@"NB %s ",__func__);
}
上面代码是否能够正常执行:
根据运行结果我们可以看出,运行的而且输出结果一样,这说明pp 和p 是同一个对象,其中的内存指向关系为:
如果我们对saySomething方法进行中进行修改如下
{
//pcls ->XZPerson 类对象
id pcls = [XZPerson class];
void *pp= &pcls;
[(__bridge id)pp saySomething];
// p -> XZPerson 实例对象
XZPerson *p = [XZPerson alloc];
[p saySomething];
}
//name 为XZPerson中只用name一个成员变量
- (void)saySomething{
NSLog(@"NB %s name==%@",__func__,self.name);
}
打印结果为:
可以看出使用p调用的name为null 但是使用pp调用的name值为<ViewController: 0x7fb4d6d064e0>
接下来我们继续探索为什么会是ViewController呢,我们对代码再次修改,在运行之前声明一个字符串
{
NSString *tem = @"Alan";
id pcls = [XZPerson class];
void *pp= &pcls;
[(__bridge id)pp saySomething];
// p -> XZPerson 实例对象
XZPerson *p = [XZPerson alloc];
[p saySomething];
NSLog(@"面试题");
}
打印结果为:
这个时候name访问的是上面的对象变量Alan;这说明地址进行了偏移取值;
总结
这篇文章描述了一些iOS中的一些面试题,希望对看的人有用处;
欢迎大家点赞,关注我的CSDN,我会定期做一些技术分享!未完待续。。。