objc_msgSend底层调用有3大阶段
1.消息发送
2.动态方法解析
void c_other(id self, SEL _cmd)
{
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
- (void)other
{
NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 获取其他方法
Method method = class_getInstanceMethod(self, @selector(other));
// 动态添加test方法的实现
class_addMethod(self, sel,
method_getImplementation(method),
method_getTypeEncoding(method));
// 第一个参数是object_getClass(self)
// class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
3.消息转发
int __forwarding__(void *frameStackPointer, int isStret) {
id receiver = *(id *)frameStackPointer;
SEL sel = *(SEL *)(frameStackPointer + 8);
const char *selName = sel_getName(sel);
Class receiverClass = object_getClass(receiver);
// 调用 forwardingTargetForSelector:
if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:)))
{
id forwardingTarget = [receiver forwardingTargetForSelector:sel];
if (forwardingTarget && forwardingTarget != receiver)
{
return objc_msgSend(forwardingTarget, sel, ...);
}
}
// 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocation
if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:)))
{
NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
if (methodSignature && class_respondsToSelector(receiverClass, @selector(forwardInvocation:)))
{
NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
[receiver forwardInvocation:invocation];
void *returnValue = NULL;
[invocation getReturnValue:&value];
return returnValue;
}
}
if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:)))
{
[receiver doesNotRecognizeSelector:sel];
}
// The point of no return.
kill(getpid(), 9);
}
一个具体的使用
/************************消息转发**************************/
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
// objc_msgSend([[MJCat alloc] init], aSelector)
return [[MJCat alloc] init]; //返回处理方法的调用对象
}
return [super forwardingTargetForSelector:aSelector];
}
/**************************上面代码没有实现时调用以下代码************************/
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
// return [NSMethodSignature signatureWithObjCTypes:"v@:i"]; 字节数可以省略
}
return [super methodSignatureForSelector:aSelector];
}
// NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation invokeWithTarget:[[MJCat alloc] init]];
}
isa_t结构体
struct {
uintptr_t nonpointer : 1;//0,代表普通的指针,存储着Class、Meta-Class对象的内存地址 1,代表优化过,使用位域存储更多的信息
uintptr_t has_assoc : 1;//是否有设置过关联对象,如果没有,释放时会更快
uintptr_t has_cxx_dtor : 1;//是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
uintptr_t shiftcls : 33; //存储着Class、Meta-Class对象的内存地址信息
uintptr_t magic : 6;//用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1;//是否有被弱引用指向过,如果没有,释放时会更快
uintptr_t deallocating : 1;//对象是否正在释放
uintptr_t has_sidetable_rc : 1;//引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
uintptr_t extra_rc : 19;//里面存储的值是引用计数器减1
};
class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
method包含:SEL name,types,方法的实现
types包含了函数返回值、参数编码的字符串
IMP代表函数的具体实现
SEL代表方法/函数名,一般叫做选择器,底层结构跟char *类似
1.可以通过@selector()和sel_registerName()获得
2.可以通过sel_getName()和NSStringFromSelector()转成字符串
3.不同类中相同名字的方法,所对应的方法选择器是相同的
Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度
hash表类似字典,一个key对应一个value,只不过hash表里面的数据根据key生成索引,能够快速的取出数据
面试题及具体应用:
讲一下 OC 的消息机制
1.OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
2.objc_msgSend底层有3大阶段:消息发送(当前类、父类中查找)、动态方法解析、消息转发
什么是Runtime?
1.OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
2.OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数,源码由C\C++\汇编语言编写
3.平时编写的OC代码,底层都是转换成了Runtime API进行调用
平时在项目中的应用
1.利用关联对象(AssociatedObject)给分类添加属性
@implementation NSObject (Property)
- (NSString *)name
{
// 根据关联的key,获取关联的值。
return objc_getAssociatedObject(self, key);
}
- (void)setName:(NSString *)name
{
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
2.遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
2.1 、遍历UITextField的私有属性,修改placeholder的颜色
unsigned int count;
Ivar *ivars = class_copyIvarList([UITextField 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);
self.textField.placeholder = @"请输入用户名";
[self.textField setValue:[UIColor redColor]forKeyPath:@"_placeholderLabel.textColor"];
UILabel *placeholderLabel = [self.textField valueForKeyPath:@"_placeholderLabel"];
placeholderLabel.textColor = [UIColor redColor];
NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
attrs[NSForegroundColorAttributeName] = [UIColor redColor];
self.textField.attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:@"请输入用户名" attributes:attrs];
2.2、字典转模型
+ (instancetype)ly_objectWithJson:(NSDictionary *)json
{
id obj = [[self alloc] init];
unsigned int count;
Ivar *ivars = class_copyIvarList(self, &count); //利用Runtime遍历所有的成员变量
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]; //利用KVC设值
}
free(ivars);
return obj;
}
3.交换方法实现
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]; //-[MJPerson test]
//替换方法的实现
class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
NSLog(@"123123");
}), "v");
+ (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);
}
类簇,交换系统类的方法实现,NSString、NSArray、NSDictionary,真实类型是其他类型
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
Class cls = NSClassFromString(@"__NSArrayM");
//Class cls = NSClassFromString(@"__NSDictionaryM"); 可变字典
// Class cls2 = NSClassFromString(@"__NSDictionaryI"); 不可变字典
Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
Method method2 = class_getInstanceMethod(cls, @selector(mj_insertObject:atIndex:));
method_exchangeImplementations(method1, method2);
});
}
4.利用消息转发机制解决方法找不到的异常问题
objc_msgSend消息发送,动态方法解析,消息转发,添加方法实现防止触发找不到方法的异常问题
5.创建一个类
// 创建类
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);
// 获取成员变量信息
Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));//_age i
// 设置和获取成员变量的值
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); //123 10
给对象设置对应的Class
MJPerson *person = [[MJPerson alloc] init];
[person run]; //[MJPerson run]
object_setClass(person, [MJCar class]);
[person run]; //[MJCar run]
Runtime API
1.类
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) //动态创建一个类(参数:父类,类名,额外的内存空间)
void objc_registerClassPair(Class cls) //注册一个类(要在类注册之前添加成员变量)
void objc_disposeClassPair(Class cls)//销毁一个类
Class object_getClass(id obj) //获取isa指向的Class
Class object_setClass(id obj, Class cls)//设置isa指向的Class
BOOL object_isClass(id obj)//判断一个OC对象是否为Class
BOOL class_isMetaClass(Class cls) //判断一个Class是否为元类
Class class_getSuperclass(Class cls)//获取父类
2.成员变量
Ivar class_getInstanceVariable(Class cls, const char *name)//获取一个实例变量信息
Ivar *class_copyIvarList(Class cls, unsigned int *outCount) //拷贝实例变量列表(最后需要调用free释放)
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)//获取成员变量的相关信息
3.属性
objc_property_t class_getProperty(Class cls, const char *name)//获取一个属性
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)//拷贝属性列表(最后需要调用free释放)
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)//获取属性的一些信息
4.方法
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) //交换方法实现
Method *class_copyMethodList(Class cls, unsigned int *outCount)//拷贝方法列表(最后需要调用free释放)
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)//动态添加方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)//动态替换方法
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) //获取方法的相关信息(带有copy的需要调用free去释放)
char *method_copyArgumentType(Method m, unsigned int index) //获取方法的相关信息(带有copy的需要调用free去释放)
const char *sel_getName(SEL sel) //选择器相关
SEL sel_registerName(const char *str)//选择器相关
IMP imp_implementationWithBlock(id block)//用block作为方法实现
id imp_getBlock(IMP anImp)//用block作为方法实现
BOOL imp_removeBlock(IMP anImp)//用block作为方法实现
bucket_t、cache_t、method_t、class_ro_t、class_rw_t数据结构
struct bucket_t {
cache_key_t _key;
IMP _imp;
};
struct cache_t {
bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
IMP imp(SEL selector)
{
mask_t begin = _mask & (long long)selector;
mask_t i = begin;
do {
if (_buckets[i]._key == 0 || _buckets[i]._key == (long long)selector) {
return _buckets[i]._imp;
}
} while ((i = cache_next(i, _mask)) != begin);
return NULL;
}
};
struct method_t {
SEL name;
const char *types;
IMP imp;
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_list_t * methods; // 方法列表
property_list_t *properties; // 属性列表
const protocol_list_t * protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
/* OC对象 */
struct objc_object {
void *isa;
};
/* 类对象 */
struct objc_class : objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
public:
class_rw_t* data() {
return bits.data();
}
objc_class* metaClass() {
return (objc_class *)((long long)isa & ISA_MASK);
}
};
/* OC对象 */
struct ly_objc_object {
void *isa;
};
/* 类对象 */
struct ly_objc_class : ly_objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
public:
class_rw_t* data() {
return bits.data();
}
ly_objc_class* metaClass() {
return (ly_objc_class *)((long long)isa & ISA_MASK);
}
};
//由于方法缓存是存在hash表中,存入的下标为方法名&cache._mask
bucket_t *buckets = cache._buckets;
bucket_t bucket = buckets[(long long)@selector(studentTest) & cache._mask];
NSLog(@"%s %p", bucket._key, bucket._imp);
//遍历方法缓存中方法的key和实现
for (int i = 0; i <= cache._mask; i++) {
bucket_t bucket = buckets[i];
NSLog(@"%s %p", bucket._key, bucket._imp);
}
[super message]的底层实现
1.消息接收者仍然是子类对象
2.从父类开始查找方法的实现
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 */
};
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接收者
__unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
};
// super调用的receiver仍然是MJStudent对象
[super run];
NSLog(@"[super class] = %@", [super class]); // MJStudent
NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson
- (void)print
{
[super print];
objc_msgSendSuper(
{
self,
[MJPerson class]
}, sel_registerName("print"));
}
objc_msgSendSuper的工作原理应该是这样的:
从objc_super结构体指向的superClass父类的方法列表开始查找selector,找到后以objc->receiver去调用父类的这个selector。注意,最后的调用者是objc->receiver,而不是super_class!