美文网首页iOS记录
runtime.h(五)

runtime.h(五)

作者: 想聽丿伱說衹愛我 | 来源:发表于2020-08-17 16:18 被阅读0次

    版本:iOS13.5

    runtime.h

    需要引入头文件#import <objc/runtime.h>
    runtime其他方法通道

    索引

    • 返回所有已加载的Objective-C框架和动态库的名称的数组。
      objc_copyImageNames
    • 返回类cls的动态库名。
      class_getImageName
    • 返回库中所有类的名称的数组。
      objc_copyClassNamesForImage
    • 返回选择器的方法的名称。
      sel_getName
    • 注册一个方法并映射到选择器。
      sel_registerName
    • 判断两个选择器是否相等。
      sel_isEqual
    • 迭代突变检测。
      objc_enumerationMutation
    • 设置突变处理函数。
      objc_setEnumerationMutationHandler
    • 设置要调用的函数。
      objc_setForwardHandler
    • 将方法实现变成调用块block实现。
      imp_implementationWithBlock
    • 返回与方法实现的指针IMP关联的块。
      imp_getBlock
    • 将IMP与其关联的块解除关联,并释放该块。
      imp_removeBlock
    • 返回由弱指针引用的对象。
      objc_loadWeak
    • 将对象存储到弱指针引用的变量中。
      objc_storeWeak
    • 为对象object设置关联的值value。
      objc_setAssociatedObject
    • 返回对象object关联的值value。
      objc_getAssociatedObject
    • 清除对象object所有的关联。
      objc_removeAssociatedObjects
    • class_getImageName安装一个钩子。
      objc_setHook_getImageName
    • objc_getClass和其相关函数安装一个钩子。
      objc_setHook_getClass
    • objc_setAssociatedObject安装一个钩子。
      objc_setHook_setAssociatedObject
    • 添加一个在加载新动态库时要调用的函数。
      objc_addLoadImageFunc

    详解

    • 返回所有已加载的Objective-C框架和动态库的名称的数组。
    const char * _Nonnull * _Nonnull objc_copyImageNames(unsigned int * _Nullable outCount) 
    

    你必须使用free释放数组。

    outCount 指针,保存数组元素的数量。

    例:
        unsigned int copyImageNamesCount = 0;
        const char **copyImageNames = objc_copyImageNames(&copyImageNamesCount);
        NSLog(@"%d", copyImageNamesCount);
        for (NSInteger i = 0; i < (MIN(copyImageNamesCount, 5)); i++) {
            const char *copyImageName = copyImageNames[i];
            NSLog(@"%s\n", copyImageName);
        }
    输出:
    215
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/introspection/libdispatch.dylib
    
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libxpc.dylib
    
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libsystem_featureflags.dylib
    
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libsystem_trace.dylib
    
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib
    
    • 返回类cls的动态库名。
    const char * _Nullable class_getImageName(Class _Nullable cls) 
    

    例子见objc_copyClassNamesForImage

    • 返回库中所有类的名称的数组。
    const char * _Nonnull * _Nullable
    objc_copyClassNamesForImage(const char * _Nonnull image,
                                unsigned int * _Nullable outCount) 
    

    image 库的名字,可通过class_getImageName获取
    outCount 指针,保存数组元素的数量。

    例: 
        const char *imageName = class_getImageName(object_getClass(self));
        NSLog(@"%s", imageName);
        unsigned int copyClassNamesForImageCount = 0;
        const char **copyClassNamesForImages = objc_copyClassNamesForImage(imageName, &copyClassNamesForImageCount);
        NSLog(@"%d", copyClassNamesForImageCount);
        for (NSInteger i = 0; i < (MIN(copyClassNamesForImageCount, 5)); i++) {
                const char *iamge = copyClassNamesForImages[i];
                NSLog(@"%s", iamge);
        }
    输出:
    /Users/XXX/Library/Developer/CoreSimulator/Devices/23EF0023-A376-4371-9C9B-30183CFCA259/data/Containers/Bundle/Application/9CDEA26A-8AD7-4BD2-A550-87F70457687A/DEMO.app/DEMO
    19
    Dispatch_blcok
    Dispatch_group
    Split1
    Dispatch_data
    ContextMenu
    
    • 返回选择器的方法的名称。
    const char * _Nonnull sel_getName(SEL _Nonnull sel)
    

    例子见sel_isEqual

    • 注册一个方法并映射到选择器。
    SEL _Nonnull sel_registerName(const char * _Nonnull str)
    

    如果对应名称的方法已经注册,则此函数仅返回对应的选择器。

    str 方法的名字
    SEL 返回该方法的选择器
    例子见sel_isEqual

    • 判断两个选择器是否相等。
    BOOL sel_isEqual(SEL _Nonnull lhs, SEL _Nonnull rhs) 
    

    sel_isEqual等价于==

    例:
        SEL registerNameSel = sel_registerName("registerName");
        SEL registerNameSel1 = sel_registerName("registerName1");
        const char *selName = sel_getName(registerNameSel);
        NSLog(@"%s", selName);
        BOOL selEqual = sel_isEqual(registerNameSel, registerNameSel1);
        NSLog(@"%@", @(selEqual));
    输出:
    registerName
    0
    
    • 迭代突变检测。
    void objc_enumerationMutation(id _Nonnull obj) 
    

    检测到突变时,编译器将插入此函数。发生突变时将调用它,并且调用enumerationMutationHandler设置的回调函数。如果未设置回调函数,则会崩溃。

    obj 突变的对象

    • 设置突变处理函数。
    void objc_setEnumerationMutationHandler(void (*_Nullable handler)(id _Nonnull )) 
    
    • 设置要调用的函数。
    void objc_setForwardHandler(void * _Nonnull fwd, void * _Nonnull fwd_stret) 
    

    方法详见#import <objc/message.h>
    fwd objc_msgForward跳转到的函数
    fwd_stret objc_msgForward_stret跳转到的函数

    上面三个方法目前还不知如何使用!-_-!

    • 将方法实现变成调用块block实现。
    IMP _Nonnull imp_implementationWithBlock(id _Nonnull block)
    

    block 实现此方法的块。其签名应为method_return_type ^(id self,method_args ...)。选择器SEL不能用作该块的参数。
    IMP 返回该方法实现的指针,可使用imp_removeBlock与关联的块解除关联,并释放该块。
    例子见imp_removeBlock

    • 返回与方法实现的指针IMP关联的块。
    id _Nullable imp_getBlock(IMP _Nonnull anImp)
    

    anImp 方法实现的指针,必须是通过imp_implementationWithBlock创建的。
    例子见imp_removeBlock

    • 将IMP与其关联的块解除关联,并释放该块。
    BOOL imp_removeBlock(IMP _Nonnull anImp)
    

    anImp 方法实现的指针,必须是通过imp_implementationWithBlock创建的。
    BOOL 如果该块已成功释放,返回YES,否则返回NO。

    例:
        typedef void (^TestBlcok)(void);
        TestBlcok block = ^ {
            NSLog(@"TestBlcok");
        };
        IMP blockIMP = imp_implementationWithBlock(block);
        BOOL addMethod = class_addMethod(object_getClass(self), NSSelectorFromString(@"blockTest"), blockIMP, "v@:@");
        NSLog(@"blockTest方法添加%@", addMethod?@"成功":@"失败");
        BOOL respondsToSelector = class_respondsToSelector(object_getClass(self), NSSelectorFromString(@"blockTest"));
        NSLog(@"%@blockTest方法", respondsToSelector?@"存在":@"不存在");
        if (respondsToSelector) {
            [self performSelector:NSSelectorFromString(@"blockTest")];
        }
        
        TestBlcok block1 = imp_getBlock(blockIMP);
        if (block1) {
            NSLog(@"block1存在");
        }
        BOOL removeSuccess = imp_removeBlock(blockIMP);
        NSLog(@"%@", @(removeSuccess));
        block1 = imp_getBlock(blockIMP);
        if (!block1) {
            NSLog(@"block1不存在");
        }
    输出:
    blockTest方法添加成功
    存在blockTest方法
    TestBlcok
    block1存在
    1
    block1不存在
    
    • 返回由弱指针引用的对象。
    id _Nullable objc_loadWeak(id _Nullable * _Nonnull location)
    

    该方法会保留并自动释放该对象以确保其保持足够长的生存期以供调用者使用。能使用__weak变量的任何地方都可以使用此函数。

    location 弱指针的地址
    例子见objc_storeWeak

    • 将对象存储到弱指针引用的变量中。
    id _Nullable objc_storeWeak(id _Nullable * _Nonnull location, id _Nullable obj) 
    

    你可以在任何为__weak变量赋值的地方使用该方法
    给weak变量赋值和取值使用objc_storeWeakobjc_loadWeak
    使用object_setIvarobject_setInstanceVariable可对变量赋值
    使用object_setIvarWithStrongDefaultobject_setInstanceVariableWithStrongDefault可对默认是strong的变量赋值
    详细可见weak和strong的理解

    location 弱指针的地址
    obj 存储的对象
    id 弱指针的变量

    例:
        //set方法 这两种set方法是等价的
        __weak NSNumber *number = [NSNumber numberWithInteger:10];
        id set = objc_storeWeak(&number, [NSNumber numberWithInteger:20]);
        //get方法 这两种get方法是等价的
        id get = number;
        id get1 = objc_loadWeak(&number);
    
    • 为对象object设置关联的值value。
    void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                             id _Nullable value, objc_AssociationPolicy policy)
    

    object 关联的源对象
    key 关联的密钥
    value 与对象的密钥相关联的值。传递nil以清除现有的关联。
    policy 关联的策略

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        //与关联对象是弱引用关系
        OBJC_ASSOCIATION_ASSIGN = 0,     
        //与关联对象是强引用关系,但关联不是原子性的
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,     
        //与关联对象是复制引用关系,但关联不是原子性的
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   
        //与关联对象是强引用关系,但关联是原子性的
        OBJC_ASSOCIATION_RETAIN = 01401,       
        //与关联对象是复制引用关系,但关联是原子性的
        OBJC_ASSOCIATION_COPY = 01403          
    };
    例子见```objc_removeAssociatedObjects```
    
    • 返回对象object关联的值value。
    id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
    

    object 关联的源对象
    key 关联的密钥
    例子见objc_removeAssociatedObjects

    • 清除对象object所有的关联。
    void objc_removeAssociatedObjects(id _Nonnull object)
    

    此方法的主要目的是使对象易于返回到无关联的“原始状态”。因此不应将其用于一般性地从对象中删除关联,因为它还会删除其他客户可能已添加到该对象中的关联。应该使用objc_setAssociatedObject(object, key, nil, policy)删除关联。

    例:
        objc_setAssociatedObject(self, @"key", @"value", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        id value = objc_getAssociatedObject(self, @"key");
        NSLog(@"%@",value);
        //这里最好是使用下面注释的方法删除关联,此处仅为了演试。
        objc_removeAssociatedObjects(self);
    //    objc_setAssociatedObject(self, @"key", nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        value = objc_getAssociatedObject(self, @"key");
        NSLog(@"%@", value);
    输出:
    value
    (null)
    
    • class_getImageName安装一个钩子。
    void objc_setHook_getImageName(objc_hook_getImageName _Nonnull newValue,
                            objc_hook_getImageName _Nullable * _Nonnull outOldValue)
    

    链中的第一个钩子是class_getImageName的本地实现。
    你应该为不能识别的类调用前一个的钩子

    newValue 要安装的钩子函数
    outOldValue 函数指针变量的地址,存储旧的钩子函数。

    typedef BOOL (*objc_hook_getImageName)(Class _Nonnull cls,
                   const char * _Nullable * _Nonnull outImageName);
    

    cls 正在被查找的类
    outImageName 指针,保存查找的结果。
    BOOL 返回YES表示已找到该类的动态库名,返回NO则表示未找到。

    例:
        objc_hook_getImageName oldGetImageName;
        if (@available(iOS 12.0, *)) {
            objc_setHook_getImageName(getImageName, &oldGetImageName);
        }
        const char *getImageName = class_getImageName(object_getClass(self));
        NSLog(@"%s", getImageName);
    
    BOOL getImageName(Class _Nonnull cls, const char * _Nullable * _Nonnull outImageName) {
        NSLog(@"%s", *outImageName);
        return YES;
    }
    输出:添加了钩子后,输出的名字无法识别。
    方法有说明 你应该为不能识别的类调用前一个的钩子 如果这样,这钩子不知意义为何。
    \M-H\^S&\^F\^A
    \M-H\^S&\^F\^A
    
    • objc_getClass和其相关函数安装一个钩子。
    void objc_setHook_getClass(objc_hook_getClass _Nonnull newValue,
                           objc_hook_getClass _Nullable * _Nonnull outOldValue)
    

    你应该为不能识别的类调用前一个的钩子

    newValue 要安装的钩子函数。
    outOldValue 函数指针变量的地址,存储旧的钩子函数。

    typedef BOOL (*objc_hook_getClass)(const char * _Nonnull name, 
                    Class _Nullable * _Nonnull outClass);
    

    name 查找的类名
    outClass 指针,保存查找的结果。
    BOOL 返回YES表示已找到该名的类,返回NO则表示未找到。

    例:
        objc_hook_getClass oldGetClass;
        if (@available(iOS 12.2, *)) {
            objc_setHook_getClass(getClass, &oldGetClass);
        }
        Class objcGetClass = objc_getClass("UIViewController");
        NSLog(@"%s", class_getName(objcGetClass));
    
    BOOL getClass(const char * _Nonnull name, Class _Nullable * _Nonnull outClass) {
        NSLog(@"%s", class_getName(*outClass));
        return YES;
    }
    输出:经测试,objc_getClass当能查出类时,不会执行下面的钩子getClass;
    当不能查出时,却会执行下面的钩子,不知为何。
    UIViewController
    
    • objc_setAssociatedObject安装一个钩子。
    void objc_setHook_setAssociatedObject(objc_hook_setAssociatedObject _Nonnull newValue,
                        objc_hook_setAssociatedObject _Nullable * _Nonnull outOldValue)
    

    您应始终调用前一个挂钩。

    newValue 要安装的钩子函数。
    outOldValue 函数指针变量的地址,存储旧的钩子函数。

    typedef void (*objc_hook_setAssociatedObject)(id _Nonnull object,
             const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy);
    

    object 查找的对象
    key 对象关联的密钥
    value 与对象的密钥相关联的值
    policy 策略

    例:
        objc_hook_setAssociatedObject oldSetAssociatedObject;
        if (@available(iOS 13.0, *)) {
            objc_setHook_setAssociatedObject(objcSetAssociatedObject, &oldSetAssociatedObject);
    //        objc_setHook_setAssociatedObject(oldSetAssociatedObject, &oldSetAssociatedObject);
        }
        objc_setAssociatedObject(self, "key", @"value", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    void objcSetAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                             id _Nullable value, objc_AssociationPolicy policy) {
        NSLog(@"%@", object);
    }
    这样写会崩溃 方法提示说 您应始终调用前一个挂钩 所以把注释打开后才正常,但这样就不会走钩子函数了,不知正确使用方法是怎样的。
    
    • 添加一个在加载新动态库时要调用的函数。
    void objc_addLoadImageFunc(objc_func_loadImage _Nonnull func)
    

    该函数是在ObjC内部运行时锁保持不变的情况下调用的。请谨慎操作该函数,以免发生死锁或性能下降。

    func 要添加的函数

    typedef void (*objc_func_loadImage)(const struct mach_header * _Nonnull header);
    

    header 详情可见mach_header

    例:
        if (@available(iOS 13.0, *)) {
            objc_addLoadImageFunc(objcFuncLoadImage);
        }
    
    void objcFuncLoadImage(const struct mach_header * _Nonnull header) {
        
    }
    

    相关文章

      网友评论

        本文标题:runtime.h(五)

        本文链接:https://www.haomeiwen.com/subject/dpuqjktx.html