iOS进阶-09面试题
2020-02-19 本文已影响0人
ricefun
RunTime是什么?
- Runtime是由C、C++、汇编实现的一套API,为OC加入了面向对象、运行时功能
- 运行时是指将数据类型的确定由编译时推迟到运行时;
- 平时编写的OC代码,在程序运行过程中,其实最终会转化成Runtime的C语言代码,Runtime是OC的幕后工作者
方法的本质是什么?Sel、Imp又是什么?两者间的关系?
方法的本质:发送消息,发送消息会有以下几个过程
1、快速查找:(objc_msgSend) -- cache_t缓存消息
2、慢速查找:递归自己|父类 -- lookUpimpOrForward
3、查找不到消息:动态方法解析 -- resolveInstanceMethod
4、消息快速转发:-- forwardingTargetForSelector
5、消息慢速转发:-- methodSignatureForSelecot & forwardInvocation
sel是方法编号 - 在read_images期间就编译进入了内存
imp就是函数的实现指针,找imp就是找函数的过程
sel相当于是书本的目录title
imp 就是书本的页码
查找具体函数就是根据title(sel)查找页码(imp)的过程,最后翻到具体的页面
能否向编译后得到的类增加实例变量?能否向运行时创建的类中添加实例变量?
1、不能向编译后得到的类增加实例变量
2、只要类没有注册到内存,还是可以添加的
原因:编译好的实例变量储存在ro中,一旦编译完成,内结构就会确定,无法修改;但是可以添加属性和方法
[self class] 和 [super class]
#import "Student.h"
@implementation Student
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@",NSStringFromClass([Student class]));
NSLog(@"%@",NSStringFromClass([super class]));
}
return self;
}
@end
打印结果
Student
Student
解析:
截屏2020-02-1915.35.33.png
[self class]就是发送消息objc_msgSend,消息接受者是self,方法编号:class
[super class]就是发送消息objc_msgSendSuper,消息接受者还是self,方法编号:class,只是objc_msgSendSuper会更快,直接跳过self的查找
weak的是如何实现自动置为nil?
- 首先系统会维护一张散列表(sidetable),这张表中又包含了弱引用表(weak_table),当然还有其他表(eg:引用计数表)
- 当一个属性被设置成weak时,weak_table表中会查找当内部有没有该对象的弱引用数组(weak_entry数组),如果有就直接插入这个属性到这个weak_entry数组,没有就先创建weak_entry数组再插入
- 当对象被释放时(delloc),会通过对象指针去查找weak_table没有该对象的weak_entry数组,有的话遍历weak_entry数组,将内部的对象置为nil;最后将这个weak_entry数组remove
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
...
...
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
strong是如何工作的?
strong内部就是向当前对象发送一个retain消息 截屏2020-02-1916.15.57.pngMethod-Swizzling
#import "LGRuntimeTool.h"
#import <objc/runtime.h>
@implementation LGRuntimeTool
+ (void)lg_bestMethodSwizzlingWithClass:(Class)cls oriSEL:(SEL)oriSEL swizzledSEL:(SEL)swizzledSEL{
if (!cls) NSLog(@"传入的交换类不能为空");
Method oriMethod = class_getInstanceMethod(cls, oriSEL);
Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
if (!oriMethod) {//判断原始方法有没有实现,如果没有那就添加一个空的block实现,你可以在block中,进行bug上传
// 在oriMethod为nil时,替换后将swizzledSEL复制一个不做任何事的空实现,代码如下:
class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){
//这里面进行bug上传
}));
}
// 一般交换方法: 交换自己有的方法 -- 走下面 因为自己有意味添加方法失败
// 交换自己没有实现的方法:
// 首先第一步:会先尝试给自己添加要交换的方法 :personInstanceMethod (SEL) -> swiMethod(IMP)
// 然后再将父类的IMP给swizzle personInstanceMethod(imp) -> swizzledSEL
//oriSEL:personInstanceMethod
//防止子类没有实现,而父类实现,相当于交换了父类的方法
BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
if (didAddMethod) {
class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else{
method_exchangeImplementations(oriMethod, swiMethod);
}
}
@end
使用
#import "LGStudent+LG.h"
#import "LGRuntimeTool.h"
#import <objc/runtime.h>
@implementation LGStudent (LG)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{//只交换一次防止在其他地方手动(load方法)导致方法又exchange回来
[LGRuntimeTool lg_betterMethodSwizzlingWithClass:self oriSEL:@selector(helloword) swizzledSEL:@selector(lg_studentInstanceMethod)];
});
}
- (void)lg_studentInstanceMethod{
NSLog(@"LGStudent分类添加的lg对象方法:%s",__func__);
[self lg_studentInstanceMethod];
}
@end