一:runtime简单介绍
默认项目中使用runtime代码是不可以的,需要进行下面设置
Project-->Build Setting--> Enable Strict Checking of objc_msgSend Calls 修改为NO即可
二:runtime涵盖内容
1.objc/message 消息发送和转发机制
objc_msgSend 负责动态消息发送
_objc_msgForward 负责消息转发
I.代码如下:
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class class;
__unsafe_unretained _Nonnull Class super_class;
};
OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_EXPORT void
method_invoke(void /* id receiver, Method m, ... */ )
OBJC_EXPORT id _Nullable
_objc_msgForward(id _Nonnull receiver, SEL _Nonnull sel, ...)
II.消息转发机制
2.objc/objc
typedef struct objc_class *Class;
struct objc_object {
Class isa;
};
typedef struct objc_object *id;
typedef struct objc_selector *SEL;
//sel和object方法
const char * _Nonnull sel_getName(SEL _Nonnull sel)
SEL _Nonnull sel_registerName(const char * _Nonnull str)
BOOL sel_isMapped(SEL _Nonnull sel)
SEL _Nonnull sel_getUid(const char * _Nonnull str)
const char * _Nonnull object_getClassName(id _Nullable obj)
void * _Nullable object_getIndexedIvars(id _Nullable obj)
3.objc/runtime runtime核心内容
I.objc_class结构体
typedef struct objc_method *Method;
typedef struct objc_ivar *Ivar;
typedef struct objc_category *Category;
typedef struct objc_property *objc_property_t;
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
II.属性,方法,变量获取常用方法
参考链接:https://www.jianshu.com/p/cefa1da5e775
代码如下:
//1.属性
u_int count;
objc_property_t *properties = class_copyPropertyList([Person class], &count);
NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i <count; i++) {
const char *propertyName = property_getName(properties[i]);
//包含类型,修饰词等内容 参考:上面链接
const char *propertyAttributes = property_getAttributes(properties[i]);
[propertiesArray addObject:[NSString stringWithUTF8String:propertyName]];
}
free(properties);
NSLog(@"----%@",propertiesArray);
//2.方法
unsigned int methodCount;
Method *methods = class_copyMethodList([Person class], &methodCount);
NSMutableArray *methodArray = [NSMutableArray arrayWithCapacity:methodCount];
for (int i = 0; i <count; i++) {
Method temp = methods[i];
IMP imp = method_getImplementation(temp);
SEL name_f = method_getName(temp);
const char *name_s = sel_getName(name_f);
int arguments = method_getNumberOfArguments(temp);
const char *encoding = method_getTypeEncoding(temp);
NSLog(@"方法名:%@,参数个数:%d,编码方式:%@",[NSString stringWithUTF8String:name_s],arguments,[NSString stringWithUTF8String:encoding]);
[methodArray addObject:[NSString stringWithUTF8String:name_s]];
}
free(methods);
NSLog(@"----%@",methodArray);
//3.全局变量
u_int ivarcount;
Ivar *ivars = class_copyIvarList([Person class], &ivarcount);
NSMutableArray *ivarArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i <ivarcount; i++) {
const char *ivarName = ivar_getName(ivars[i]);
[ivarArray addObject:[NSString stringWithUTF8String:ivarName]];
}
free(ivars);
NSLog(@"----%@",ivarArray);
III.方法交换常用的经典代码
+ (void)load {
Class class = [self class];
SEL origSEL = @selector(viewWillAppear:);
SEL replaceSEL = @selector(viewWillAppear:);
Method originMethod = class_getInstanceMethod(class, origSEL);
Method replaceMethod = class_getInstanceMethod(class, replaceSEL);
IMP origIMP = class_getMethodImplementation_stret(class, origSEL);
IMP replaceIMP = class_getMethodImplementation_stret(class, replaceSEL);
//先为原始方法添加实现,false有实现方法,true没有实现方法
BOOL isAdd = class_addMethod(class, origSEL,replaceIMP , "v@:");
if (isAdd) {
//此时两个方法名字皆指向同一个实现方法,下面为新方法变更实现方法
class_replaceMethod(class, replaceSEL,origIMP , "v@:");
}else{
//交换两个方法的实现
method_exchangeImplementations(originMethod, replaceMethod);
}
}
- (void)viewWillAppearN:(BOOL)animated {
//调用系统方法
[self viewWillAppearN:animated];
NSString *name = [NSString stringWithUTF8String:object_getClassName(self)];
NSString *trackName = [NSString stringWithFormat:@"%@----viewWillAppear",name];
[[TrackObject sharedManager]track:trackName];
}
按钮事件监听[是UIControl的方法 sendAction:to:forEvent:]
+ (void)load {
Class class = [self class];
SEL origSEL = @selector(sendAction:to:forEvent:);
SEL replaceSEL = @selector(sendActionN:to:forEvent:);
Method originMethod = class_getInstanceMethod(class, origSEL);
Method replaceMethod = class_getInstanceMethod(class, replaceSEL);
IMP origIMP = class_getMethodImplementation_stret(class, origSEL);
IMP replaceIMP = class_getMethodImplementation_stret(class, replaceSEL);
//先为原始方法添加实现,false有实现方法,true没有实现方法
BOOL isAdd = class_addMethod(class, origSEL,replaceIMP , "v@:");
if (isAdd) {
//此时两个方法名字皆指向同一个实现方法,下面为新方法变更实现方法
class_replaceMethod(class, replaceSEL,origIMP , "v@:");
}else{
//交换两个方法的实现
method_exchangeImplementations(originMethod, replaceMethod);
}
}
- (void)sendActionN:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event {
[self sendActionN:action to:target forEvent:event];
NSString *trackName = [NSString stringWithFormat:@"%@----%@",target,NSStringFromSelector(action)];
[[TrackObject sharedManager]track:trackName];
}
其他的参照WOCrashProtector,里面的内容很全
4.objc/NSObject 最基础类NSObject介绍
三:项目应用场景
1.防止项目闪退
WOCrashProtector 就是很好的防止闪退的依赖库
2.代码无缝埋点
3.类别中设置属性实现
4.切面编程的核心
5.开启动态创建类,让你代码有逼格【装逼专用,项目中勿用】
- (void)viewDidLoad {
[super viewDidLoad];
//创建类对象
Class newClass = objc_allocateClassPair([NSObject class], "EdenModel", 0);
class_addMethod(newClass, @selector(test), (IMP) test, "v@:"); //添加方法
NSString*name =@"name";
class_addIvar(newClass, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id)); //添加属性
objc_registerClassPair(newClass); //注册此类对象,至关重要
//开始用这个类
NSObject *edenModelInstance = [NSClassFromString(@"EdenModel") new];
[edenModelInstance setValue:@"杰克" forKey:@"name"];
NSLog(@"我的名字:---%@",[edenModelInstance valueForKey:@"name"]); //我的名字:---杰克
[edenModelInstance performSelector:@selector(test)];
objc_msgSend(edenModelInstance, @selector(test)); //通过消息发送调用方法
}
//方法的实现
void test(id self, SEL _cmd){
NSLog(@"This object is %p.",self); //edenModelInstance
NSLog(@"Class is %@, and super is %@.",[self class],[self superclass]); //edenModelInstance
Class currentClass = [self class];
for( int i = 1; i < 5; ++i )
{
NSLog(@"Following the isa pointer %d times gives %p",i,currentClass);
currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject's class is %p", [NSObject class]); //NSObject 0x7fff89be1d00
NSLog(@"NSObject's meta class is %p",object_getClass([NSObject class])); //NSObject 0x7fff89be1cd8
}
四:第三方库有关runtime知识源码探索
1.MJExtension
2.Aspects
3.jrswizzle
4.UIViewController-Swizzled
五:额外相关知识load,initialize和init
load 在程序启动前初始化执行一次,且只执行一次
initialize 在init前初始化执行一次,且只执行一次
init 为实例方法显而易见,每调用一次执行一次
-
区别及调用顺序
参考链接: https://www.jianshu.com/p/d2ba735fa0bd
https://www.jianshu.com/p/939765be93a7int main(int argc, char * argv[]) { NSLog(@"%s:1",__func__);//第一个监测点 NSString * appDelegateClassName; @autoreleasepool { NSLog(@"%s:2",__func__);//第二个监测点 appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName); } //AView.m @implementation AView + (void)load { NSLog(@"%s",__func__); } + (void)initialize { NSLog(@"%s",__func__); } -(instancetype )init{ if (self = [super init]) { NSLog(@"%s",__func__); } return self; } @end
执行结果如下:
iOSAnimation[17421:357707] +[AView load]
iOSAnimation[17421:357707] main:1
iOSAnimation[17421:357707] main:2
iOSAnimation[17421:357707] +[AView initialize]
iOSAnimation[17421:357707] -[AView init]
iOSAnimation[17421:357707] -[AView init]
因此load里面的代码不可过多,否则会造成启动时间过长
load和initialize在父类和子类中执行的顺序
load 调用 父类-->子类-->类别
initialize调用 (无类别)父类-->子类 有类别 父类类别-->子类类别 有类别方法会覆盖本类的方法
代码执行结果如下:
+[Person load]
+[Son load]
+[Person(Extention) load]
main
+[Person(Extention) initialize] Person
-[Person init]
+[Person(Extention) initialize] Son
+[Son initialize] Son
-[Person init]
-[Son init]