1、类的方法 - 分类的方法重名,谁先调用
一般情况下分类先调用,load是先主类再分类
void
load_images(const char *path __unused, const struct mach_header *mh)
{
...
// Discover load methods
//发现load方法
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
//加载非懒加载类
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
//加载分类的所有非懒加载
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);//寻找父类
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
if (loadable_classes_used == loadable_classes_allocated) {//扩容
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;//记录下来
loadable_classes_used++;
}
找到所有的load方法,然后就是call_load_methods
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
2.runtime是什么
runtime 是由C 和C++ 汇编 实现的⼀套API,为OC语⾔加⼊了⾯向对象,运⾏时的功能。
运⾏时(Runtime)是指将数据类型的确定由编译时推迟到了运⾏时 -
举例⼦: extension - category 的区别。
平时编写的OC代码,在程序运⾏过程中,其实最终会转换成Runtime的C语⾔代码,Runtime
是 Object-C
的幕后⼯作者
3.⽅法的本质,sel是什么?IMP是什么?两者之间的关系⼜是什么?
⽅法的本质:发送消息 , 消息会有以下⼏个流程
1:快速查找 (objc_msgSend)~ cache_t 缓存消息
2:慢速查找~ 递归⾃⼰| ⽗类 ~ lookUpImpOrForward
3:查找不到消息: 动态⽅法解析 ~ resolveInstanceMethod
4:消息快速转发~ forwardingTargetForSelector
5:消息慢速转发~ methodSignatureForSelector & forwardInvocation
sel 是⽅法编号 ~ 在read_images 期间就编译进⼊了内存
imp 就是我们函数实现指针 ,找imp 就是找函数的过程
sel 就相当于书本的⽬录 tittle
imp 就是书本的⻚码
查找具体的函数就是想看这本书⾥⾯具体篇章的内容
1:我们⾸先知道想看什么 ~ tittle (sel)
2:根据⽬录对应的⻚码 (imp) 3:翻到具体的内容
4.[self class]和[super class]的区别以及原理分析
- [self class] 就是发送消息objc_msgSend,消息接受者是 self ⽅法编号:class
- [super class] 本质就是objc_msgSendSuper, 消息的接受者还是
self ⽅法编号:class
注:只是objc_msgSendSuper 会更快 直接跳过 self 的查找
((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))
objc_msgSendSuper(
{(id)self, (id)class_getSuperclass(objc_getClass("LGTeacher"))},
sel_registerName("class")));
5.内存平移问题
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString* kc_name;
@property (nonatomic, copy) NSString *kc_hobby; // 12
- (void)saySomething;
@end
@implementation LGPerson
- (void)saySomething{ // self 消息接受者 - LGPerson: 0x7ffee8a7c0e8
NSLog(@"%s - %@",__func__,self.kc_name);
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
Class cls = [LGPerson class];
void *kc = &cls; //
LGPerson *person = [LGPerson alloc];
[(__bridge id)kc saySomething];
[person saySomething]; // self.kc_name = nil - (null)
}
打印结果为
-[LGPerson saySomething] - <ViewController: 0x7fd98dd06fe0>
-[LGPerson saySomething] - (null)
内存平移.png
kc_name
是person
首地址平移8字节获取到的,kc平移8地址是什么呢?首先栈内存是从高地址到低地址分配,参数会从前往后一直压入,而结构体是后面的先插入。所以一开始插入的是
self
,_cmd
,然后调用 [super viewDidLoad]
,super
在上面已经说过是个objc_msgSendSuper
,这里传入了一个结构体{(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}
,所以后面是(id)class_getSuperclass(objc_getClass("ViewController"))
,在后面是self
,cls
,kc
,而kc
是指向cls
地址.我们可以打印下
void *sp = (void *)&self;
void *end = (void *)&kc;
long count = (sp - end) / 0x8;
for (long i = 0; i<count; i++) {
void *address = sp - 0x8 * i;
if ( i == 1) {
NSLog(@"%p : %s",address, *(char **)address);
}else{
NSLog(@"%p : %@",address, *(void **)address);
}
}
打印结果
2020-10-23 17:57:09.267540+0800 004-内存平移问题[6622:370762] 0x7ffee3040098 - 0x7ffee30400a8
2020-10-23 17:57:09.267699+0800 004-内存平移问题[6622:370762] 0x7ffee30400c8 : <ViewController: 0x7f807a308fd0>
2020-10-23 17:57:09.267789+0800 004-内存平移问题[6622:370762] 0x7ffee30400c0 : viewDidLoad
2020-10-23 17:57:09.267883+0800 004-内存平移问题[6622:370762] 0x7ffee30400b8 : ViewController
2020-10-23 17:57:09.267977+0800 004-内存平移问题[6622:370762] 0x7ffee30400b0 : <ViewController: 0x7f807a308fd0>
2020-10-23 17:57:09.268061+0800 004-内存平移问题[6622:370762] 0x7ffee30400a8 : LGPerson
所以kc
中的self.kc_name
指向到的就是self(ViewController)
6.Method-Swizzling注意事项
我们平时最常用的runtime
方法就是methodSwizzling
,但是这里却有很多坑点。
我们为了一次性处理方法交换,基本都是放在load
方法中,但我们前文中就说过,这回加载整个类,阻碍启动,而且也可以手动调用load
方法。所以我们可以放在initial
中,只有第一次就收消息的时候才会处理。
我们在交换方法的时候也要检测下时候有交换方法的实现
+ (void)lg_betterMethodSwizzlingWithClass:(Class)cls oriSEL:(SEL)oriSEL swizzledSEL:(SEL)swizzledSEL{
if (!cls) NSLog(@"传入的交换类不能为空");
Method oriMethod = class_getInstanceMethod(cls, oriSEL);
Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
// 一般交换方法: 交换自己有的方法 -- 走下面 因为自己有意味添加方法失败
// 交换自己没有实现的方法:
// 首先第一步:会先尝试给自己添加要交换的方法 :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);
}
}
class_addMethod
:如果发现方法已经存在,会失败返回,也可以用来做检查用,我们这里是为了避免源方法没有实现的情况;如果方法没有存在,我们则先尝试添加被替换的方法的实现
- 如果返回成功:则说明被替换方法没有存在.也就是被替换的方法没有被实现,我们需要先把这个方法实现,然后再执行我们想要的效果,用我们自定义的方法去替换被替换的方法. 这里使用到的是
class_replaceMethod
这个方法.class_replaceMethod
本身会尝试调用class_addMethod
和method_setImplementation
,所以直接调用class_replaceMethod
就可以了)。class_replaceMethod
是替换某个类的方法的实现,功能上可以替代class_addMethod
, 但是class_addMethod
只能在SEL
没有IMP
指向时才可以添加成功,而class_replaceMethod
不管SEL
有没有IMP
实现,都可以添加成功。 - 如果返回失败:则说明被替换方法已经存在.直接将两个方法的实现交换即
但这种时候,如果oriMethod
没有实现,调用方法的时候就会死循环,所以我们必须价格判断,给他一个空的实现。
+ (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) { // 避免动作没有意义
// 在oriMethod为nil时,替换后将swizzledSEL复制一个不做任何事的空实现,代码如下:
class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){
NSLog(@"来了一个空的 imp");
}));
}
// 一般交换方法: 交换自己有的方法 -- 走下面 因为自己有意味添加方法失败
// 交换自己没有实现的方法:
// 首先第一步:会先尝试给自己添加要交换的方法 :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);
}
}
网友评论