Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
Objective-C的动态性是由Runtime API来支撑的
Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
isa详解
具体参考isa详解
class的结构
查看源码
- 找到了这个bits
- 找到了这个class_rw_t 说明了这个 data = bits.data(); 是调用了data()方法的
- 找到这个data()函数的定义是 这样的
- 相当于 class_rw_t *data() = bits & FAST_DATA_MASK就是bits与运算这个掩码强转后的值。
- 找到这个class_rw_t的结构体
- 里面就有这些了
class_rw_t
- class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
class_ro_t
- class_ro_t 源码定义 找到了 baseMethodList
- 同时我也发现了baseMethods其实这两个是一样的指向同一个地址的指针。
- class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
- 创建时是先创建的ro_t然后才创建的rw_t
method_t
Type Encoding
具体查看这个文章
method_t详解
方法缓存
- 方法是存放在一个二维数组中的,调用方法就是进行遍历查找再执行,但是一个方法多次被调用显然这样速度很慢
方法缓存原理
objc_msgSend执行流程
- OC中的方法调用,其实都是转换为objc_msgSend函数的调用
- objc_msgSend的执行流程可以分为3大阶段
- 消息发送
- 动态方法解析
- 消息转发
objc_msgSend执行流程 – 源码跟读
- 查找流程参考
objc_msgSend执行流程01-消息发送
- 如果是从class_rw_t中查找方法
- 已经排序的,二分查找
- 没有排序的,遍历查找
- receiver通过isa指针找到receiverClass
- receiverClass通过superclass指针找到superClass 消息发送详解
objc_msgSend执行流程02-动态方法解析
- 开发者可以实现以下方法,来动态添加方法实现
- +resolveInstanceMethod:
- +resolveClassMethod:
- 动态解析过后,会重新走“消息发送”的流程
- “从receiverClass的cache中查找方法”这一步开始执行
动态添加方法
objc_msgSend的执行流程03-消息转发
- 开发者可以在forwardInvocation:方法中自定义任何逻辑
- 以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-) 消息转发
生成NSMethodSignature
super的本质
- [super message]的底层实现
- 1.消息接受者任然是子类对象
- 2.从父类开始查找方法的实现
- class 还是return object_getClass(self) 谁是消息接收者就返回谁
- superClass == return class_getSuperclass(object_getClass(self));
LLVM的中间代码(IR)
-
Objective-C在变为机器代码之前,会被LLVM编译器转换为中间代码(Intermediate Representation)
-
可以使用以下命令行指令生成中间代码
- clang -emit-llvm -S main.m
-
语法简介
- @ - 全局变量
- % - 局部变量
- alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
- i32 - 32位4字节的整数
- align - 对齐
- load - 读出,store 写入
- icmp - 两个整数值比较,返回布尔值
- br - 选择分支,根据条件来转向label,不根据条件跳转的话类似 goto
- label - 代码标签
- call - 调用函数
具体可以参考官方文档:llvm.org/docs/LangRe…
- 文件转汇编
应用
Runtime应用01 - 查看私有成员变量
- 设置UITextField 占位文字的颜色 (但是现在ios好像可以直接获取这个值了多了这个属性了)以前没有的时候是这么用的。
self.textField.placeholder = @"请输入用户名";
[self.textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
Runtime的应用02 – 字典转模型
- 利用Runtime遍历所有的属性或者成员变量
- 利用KVC设值 MJ_Extention的基本实现
+ (instancetype)mj_objectWithJson:(NSDictionary *)json
{
id obj = [[self alloc] init];
unsigned int count;
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[i];
NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
[name deleteCharactersInRange:NSMakeRange(0, 1)];
// 设值
id value = json[name];
if ([name isEqualToString:@"ID"]) {
value = json[@"id"];
}
[obj setValue:value forKey:name];
}
free(ivars);
return obj;
}
Runtime的应用02 – 替换方法实现
- class_replaceMethod
- method_exchangeImplementations
-方法替换
MJPerson *person = [[MJPerson alloc] init];
// class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v");
class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
NSLog(@"123123");
}), "v");
[person run];
-方法交换
MJPerson *person = [[MJPerson alloc] init];
Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run));
Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test));
method_exchangeImplementations(runMethod, testMethod);
[person run];
API
Runtime API01 – 类
- 动态创建一个类(参数:父类,类名,额外的内存空间)
- Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
- 注册一个类(要在类注册之前添加成员变量)
- void objc_registerClassPair(Class cls)
- 销毁一个类
- void objc_disposeClassPair(Class cls)
void testClass()
{
// 创建类
Class newClass = objc_allocateClassPair([NSObject class], "MJDog", 0);
class_addIvar(newClass, "_age", 4, 1, @encode(int));
class_addIvar(newClass, "_weight", 4, 1, @encode(int));
class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
// 注册类
objc_registerClassPair(newClass);
// MJPerson *person = [[MJPerson alloc] init];
// object_setClass(person, newClass);
// [person run];
// id dog = [[newClass alloc] init];
// [dog setValue:@10 forKey:@"_age"];
// [dog setValue:@20 forKey:@"_weight"];
// [dog run];
//
// NSLog(@"%@ %@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]);
// 在不需要这个类时释放
objc_disposeClassPair(newClass);
}
- 获取isa指向的Class
- Class object_getClass(id obj)
- Class object_getClass(id obj)
- 设置isa指向的Class
- Class object_setClass(id obj, Class cls)
- 修改了实例对象的类对象
- 判断一个OC对象是否为Class
- BOOL object_isClass(id obj)
- 元类对象是特殊的类对象
- 判断一个Class是否为元类
- BOOL class_isMetaClass(Class cls)
- 获取父类
- Class class_getSuperclass(Class cls)
Runtime API02 – 成员变量
-
获取一个实例变量信息
- Ivar class_getInstanceVariable(Class cls, const char *name)
-
拷贝实例变量列表(最后需要调用free释放)
- Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
-
设置和获取成员变量的值
- void object_setIvar(id obj, Ivar ivar, id value)
- id object_getIvar(id obj, Ivar ivar)
-
动态添加成员变量(已经注册的类是不能动态添加成员变量的)
- BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
-
获取成员变量的相关信息
- const char *ivar_getName(Ivar v)
- const char *ivar_getTypeEncoding(Ivar v)
// 获取成员变量信息
// Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
// NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
// 设置和获取成员变量的值
// Ivar nameIvar = class_getInstanceVariable([MJPerson class], "_name");
//
// MJPerson *person = [[MJPerson alloc] init];
// object_setIvar(person, nameIvar, @"123");
// object_setIvar(person, ageIvar, (__bridge id)(void *)10);
// NSLog(@"%@ %d", person.name, person.age);
// 成员变量的数量
unsigned int count;
Ivar *ivars = class_copyIvarList([MJPerson class], &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[i];
NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
}
free(ivars);
Runtime API03 – 属性
- 获取一个属性
- objc_property_t class_getProperty(Class cls, const char *name)
- 拷贝属性列表(最后需要调用free释放)
- objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
- 动态添加属性
- BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
- 动态替换属性
- void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
- 获取属性的一些信息
- const char *property_getName(objc_property_t property)
- const char *property_getAttributes(objc_property_t property)
Runtime API04 – 方法
- 获得一个实例方法、类方法
- Method class_getInstanceMethod(Class cls, SEL name)
- Method class_getClassMethod(Class cls, SEL name)
- 方法实现相关操作
- IMP class_getMethodImplementation(Class cls, SEL name)
- IMP method_setImplementation(Method m, IMP imp)
- void method_exchangeImplementations(Method m1, Method m2)
- 拷贝方法列表(最后需要调用free释放)
- Method *class_copyMethodList(Class cls, unsigned int *outCount)
- 动态添加方法
- BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
- 动态替换方法
- IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
- 获取方法的相关信息(带有copy的需要调用free去释放)
- SEL method_getName(Method m)
- IMP method_getImplementation(Method m)
- const char *method_getTypeEncoding(Method m)
- unsigned int method_getNumberOfArguments(Method m)
- char *method_copyReturnType(Method m)
- char *method_copyArgumentType(Method m, unsigned int index)
- 选择器相关
- const char *sel_getName(SEL sel)
- SEL sel_registerName(const char *str)
- 用block作为方法实现
- IMP imp_implementationWithBlock(id block)
- id imp_getBlock(IMP anImp)
- BOOL imp_removeBlock(IMP anImp)
Runtime方法交换
-
交换了两个方法的imp地址
-
交换地址并清空缓存
-
Runtime hook 工具类
-
UIControl+Extension
#import <UIKit/UIKit.h>
@interface UIControl (Extension)
@end
#import "UIControl+Extension.h"
#import <objc/runtime.h>
@implementation UIControl (Extension)
+ (void)load
{
// hook:钩子函数
Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method method2 = class_getInstanceMethod(self, @selector(mj_sendAction:to:forEvent:));
method_exchangeImplementations(method1, method2);
}
- (void)mj_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
NSLog(@"%@-%@-%@", self, target, NSStringFromSelector(action));
// 调用系统原来的实现
[self mj_sendAction:action to:target forEvent:event];
// [target performSelector:action];
// if ([self isKindOfClass:[UIButton class]]) {
// // 拦截了所有按钮的事件
//
// }
}
@end
- NSMutableArray+Extension
#import <Foundation/Foundation.h>
@interface NSMutableArray (Extension)
@end
#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>
@implementation NSMutableArray (Extension)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
Class cls = NSClassFromString(@"__NSArrayM");
Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
Method method2 = class_getInstanceMethod(cls, @selector(mj_insertObject:atIndex:));
method_exchangeImplementations(method1, method2);
});
}
- (void)mj_insertObject:(id)anObject atIndex:(NSUInteger)index
{
if (anObject == nil) return;
[self mj_insertObject:anObject atIndex:index];
}
@end
- NSMutableDictionary+Extension
#import <Foundation/Foundation.h>
@interface NSMutableDictionary (Extension)
@end
#import "NSMutableDictionary+Extension.h"
#import <objc/runtime.h>
@implementation NSMutableDictionary (Extension)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"__NSDictionaryM");
Method method1 = class_getInstanceMethod(cls, @selector(setObject:forKeyedSubscript:));
Method method2 = class_getInstanceMethod(cls, @selector(mj_setObject:forKeyedSubscript:));
method_exchangeImplementations(method1, method2);
Class cls2 = NSClassFromString(@"__NSDictionaryI");
Method method3 = class_getInstanceMethod(cls2, @selector(objectForKeyedSubscript:));
Method method4 = class_getInstanceMethod(cls2, @selector(mj_objectForKeyedSubscript:));
method_exchangeImplementations(method3, method4);
});
}
- (void)mj_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key
{
if (!key) return;
[self mj_setObject:obj forKeyedSubscript:key];
}
- (id)mj_objectForKeyedSubscript:(id)key
{
if (!key) return nil;
return [self mj_objectForKeyedSubscript:key];
}
@end