iOS记录

runtime.h(五)

2020-08-17  本文已影响0人  想聽丿伱說衹愛我

版本:iOS13.5

runtime.h

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

索引

详解

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
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跳转到的函数

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

IMP _Nonnull imp_implementationWithBlock(id _Nonnull block)

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

id _Nullable imp_getBlock(IMP _Nonnull anImp)

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

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);
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```
id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

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

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)
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
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
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) {
    
}
上一篇下一篇

猜你喜欢

热点阅读