版本: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(©ImageNamesCount);
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, ©ClassNamesForImageCount);
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_storeWeak
和objc_loadWeak
使用object_setIvar
和object_setInstanceVariable
可对变量赋值
使用object_setIvarWithStrongDefault
和object_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) {
}
网友评论