写在前面 本文主要是总结一下在我们日常项目中会用到的一些关于Runtime
的相关API,便于以后查阅。
isMemberOfClass 和 isKindOfClass 区别 在正式总结Runtime
下相关API之前,先看看isMemberOfClass
和 isKindOfClass
的区别:
- (BOOL )isMemberOfClass:(Class)cls; + (BOOL )isMemberOfClass:(Class)cls; - (BOOL )isKindOfClass:(Class)cls; + (BOOL )isKindOfClass:(Class)cls;
我们来看一下这几个方法的底层实现:
- (BOOL )isMemberOfClass:(Class)cls { return [self class ] == cls; } + (BOOL )isMemberOfClass:(Class)cls { return self ->ISA() == cls; } - (BOOL )isKindOfClass:(Class)cls { for (Class tcls = [self class ]; tcls; tcls = tcls->getSuperclass()) { if (tcls == cls) return YES ; } return NO ; } + (BOOL )isKindOfClass:(Class)cls { for (Class tcls = self ->ISA(); tcls; tcls = tcls->getSuperclass()) { if (tcls == cls) return YES ; } return NO ; }
例1:
@interface Person : NSObject @end @implementation Person @end @interface Student : Person @end @implementation Student @end int main(int argc, const char * argv[]) { @autoreleasepool { Student *student = [[Student alloc] init]; NSLog (@"1 - %d" ,[student isMemberOfClass:[Student class ]]); NSLog (@"2 - %d" ,[student isKindOfClass:[Student class ]]); NSLog (@"3 - %d" ,[student isMemberOfClass:[Person class ]]); NSLog (@"4 - %d" ,[student isKindOfClass:[Person class ]]); } return 0 ; }
打印结果:
2023 -01 -31 16 :21 :04.801267 +0800 SuperDemo[32839 :16964367 ] 1 - 1 2023 -01 -31 16 :21 :04.801310 +0800 SuperDemo[32839 :16964367 ] 2 - 1 2023 -01 -31 16 :21 :04.801347 +0800 SuperDemo[32839 :16964367 ] 3 - 0 2023 -01 -31 16 :21 :04.801382 +0800 SuperDemo[32839 :16964367 ] 4 - 1
例2:
@interface Person : NSObject @end @implementation Person @end @interface Student : Person @end @implementation Student @end int main(int argc, const char * argv[]) { @autoreleasepool { Student *student = [[Student alloc] init]; NSLog (@"1 - %d" ,[Student isMemberOfClass:object_getClass([Student class ])]); NSLog (@"2 - %d" ,[Student isKindOfClass:object_getClass([Student class ])]); NSLog (@"3 - %d" ,[Student isMemberOfClass:object_getClass([Person class ])]); NSLog (@"4 - %d" ,[Student isKindOfClass:object_getClass([Person class ])]); } return 0 ; }
打印结果:
2023 -01 -31 16 :54 :18.561752 +0800 SuperDemo[33188 :16979322 ] 1 - 1 2023 -01 -31 16 :54 :18.562298 +0800 SuperDemo[33188 :16979322 ] 2 - 1 2023 -01 -31 16 :54 :18.562390 +0800 SuperDemo[33188 :16979322 ] 3 - 0 2023 -01 -31 16 :54 :18.562430 +0800 SuperDemo[33188 :16979322 ] 4 - 1
注意 :
[Student isKindOfClass:[NSObject class ]];
结果返回的是:YES 因为NSObject的isa指向的是自己。
Runtime下的一些常用API 类 Class objc_allocateClassPair(Class _Nullable __unsafe_unretained superclass, const char * _Nonnull name, size_t extraBytes) void objc_registerClassPair(Class _Nonnull __unsafe_unretained cls) void objc_disposeClassPair(Class _Nonnull __unsafe_unretained cls) Class object_getClass(id _Nullable obj) Class object_setClass(id _Nullable obj, Class _Nonnull __unsafe_unretained cls) object_isClass(id _Nullable obj) class_isMetaClass(Class _Nullable __unsafe_unretained cls) class_getSuperclass(Class _Nullable __unsafe_unretained cls)
成员变量 Ivar class_getInstanceVariable(Class _Nullable __unsafe_unretained cls, const char * _Nonnull name) Ivar *class_copyIvarList(Class _Nullable __unsafe_unretained cls, unsigned int * _Nullable outCount) void object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)id object_getIvar(id _Nullable obj, Ivar _Nonnull ivar)BOOL class_addIvar(Class _Nullable __unsafe_unretained cls, const char * _Nonnull name, size_t size, uint8_t alignment, const char * _Nullable types)const char *ivar_getName(Ivar _Nonnull v)const char *ivar_getTypeEncoding(Ivar _Nonnull v)
方法 Method class_getInstanceMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name) Method class_getClassMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name) IMP class_getMethodImplementation(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name) IMP method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) Method *class_copyMethodList(Class _Nullable __unsafe_unretained cls, unsigned int * _Nullable outCount) BOOL class_addMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)IMP class_replaceMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types) SEL method_getName(Method _Nonnull m) IMP method_getImplementation(Method _Nonnull m) const char *method_getTypeEncoding(Method _Nonnull m)unsigned int method_getNumberOfArguments(Method _Nonnull m)char *method_copyReturnType(Method _Nonnull m)char *method_copyArgumentType(Method _Nonnull m, unsigned int index)const char *sel_getName(SEL _Nonnull sel)SEL sel_registerName(const char * _Nonnull str) IMP imp_implementationWithBlock(id _Nonnull block) id imp_getBlock(IMP _Nonnull anImp)BOOL imp_removeBlock(IMP _Nonnull anImp)
如何拦截按钮的点击事件 hook:可以理解为就是方法交换。
按钮的点击事件 会调用到UIControl的- (void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event
这个方法。
拦截按钮的点击我们只需要拦截sendAction
即可。
+ (void )load { Method method1 = class_getInstanceMethod(self , @selector (sendAction:to:forEvent:)); Method method2 = class_getInstanceMethod(self , @selector (ww_sendAction:to:forEvent:)); method_exchangeImplementations(method1, method2); } - (void )ww_sendAction:(SEL)action to:(id )target forEvent:(UIEvent *)event { if ([self isKindOfClass:UIButton .class]) { NSLog (@"我成功hook了按钮..." ); } [self ww_sendAction:action to:target forEvent:event]; }
Runtime总结 Runtime在实际开发中可能用到的场景:
写在最后 关于Runtime中常用的一些常用的API就总结到这里,如有错误请多多指教。