iOS Runtime详解与应用

一: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

  1. load 在程序启动前初始化执行一次,且只执行一次

  2. initialize 在init前初始化执行一次,且只执行一次

  3. init 为实例方法显而易见,每调用一次执行一次

  4. 区别及调用顺序
    参考链接: https://www.jianshu.com/p/d2ba735fa0bd
    https://www.jianshu.com/p/939765be93a7

    int 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]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容