动态性

YVTU

OC运行时

Objective-C Runtime 是 Objective-C 语言的核心部分,它是一套 C 语言库,负责实现动态特性,如动态类型检查、动态消息传递、动态方法解析等。Objective-C 作为一种动态语言,在编译时并不会将所有信息都确定下来,而是通过 Runtime 来在运行时处理对象和消息的调用。

在 Objective-C 中,方法调用实际上是通过向对象发送消息来实现的。Runtime 会在运行时决定哪个方法对应于这个消息,并调用相应的方法。Objective-C 允许在运行时检查对象的类型,并根据对象的实际类型执行操作。这种灵活性使得代码更具适应性和扩展性。当一个对象接收到无法处理的消息时,Runtime 提供了钩子方法,可以在运行时添加相应的实现,动态解析该方法。通过 Runtime,你可以动态地给类添加关联对象,使得在不修改类源码的情况下为对象添加额外的属性。

Objective-C Runtime 的常用运用有哪些呢?

方法交换 (Method Swizzling)

方法交换是指在运行时交换两个方法的实现。这种技术通常用于 AOP(面向切面编程),例如拦截系统方法以插入自定义逻辑。

1
2
3
Method originalMethod = class_getInstanceMethod([self class], @selector(viewWillAppear:));
Method swizzledMethod = class_getInstanceMethod([self class], @selector(xxx_viewWillAppear:));
method_exchangeImplementations(originalMethod, swizzledMethod);

动态添加方法

你可以使用 class_addMethod 动态给类添加方法。这在需要扩展类功能但无法修改类源码时非常有用。

1
2
3
4
void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(@"This is a dynamically added method.");
}
class_addMethod([self class], @selector(dynamicMethod), (IMP)dynamicMethodIMP, "v@:");

获取类信息

通过 Runtime,可以获取类的各种信息,如属性列表、方法列表、协议列表等,这些信息有助于在运行时对类进行动态操作。

1
2
3
4
5
6
7
unsigned int count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
const char *propertyName = property_getName(properties[i]);
NSLog(@"Property: %s", propertyName);
}
free(properties);

关联对象

Runtime 允许为对象动态关联数据。这在扩展现有类时非常有用,尤其是在不想子类化的情况下。

1
2
3
const char *key = "associatedObject";
objc_setAssociatedObject(self, key, anObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
id associatedObject = objc_getAssociatedObject(self, key);

消息转发 (Message Forwarding)

1
2
3
4
5
6
7
8
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = [anInvocation selector];
if ([someOtherObject respondsToSelector:sel]) {
[anInvocation invokeWithTarget:someOtherObject];
} else {
[super forwardInvocation:anInvocation];
}
}

Swift运行时

Mirror 应用场景

Mirror反射API主要用于以下几个方面:

  1. 动态获取对象的类型信息:通过Mirror反射API,可以动态获取对象的类型信息,包括类型名称、属性信息等。
  2. 遍历对象的属性:通过Mirror反射API,可以遍历对象的属性,获取属性名称和值等信息。
  3. 实现自定义调试输出:通过Mirror反射API,可以实现自定义的调试输出,将对象的属性信息格式化输出到控制台或日志中。

Mirror反射API在Swift中具有广泛的应用场景,特别是在调试和日志输出等方面。通过Mirror反射API,可以方便地获取对象的类型信息和属性信息,帮助开发者更好地理解和调试代码。

Mirror 优缺点

Mirror反射API的优点主要包括以下几个方面:

  1. 动态获取类型信息:Mirror反射API可以动态获取对象的类型信息,包括类型名称、属性信息等。
  2. 遍历对象属性:Mirror反射API可以遍历对象的属性,获取属性名称和值等信息。
  3. 实现自定义调试输出:Mirror反射API可以实现自定义的调试输出,将对象的属性信息格式化输出到控制台或日志中。

Mirror反射API的缺点主要包括以下几个方面:

  1. 性能开销:Mirror反射API会带来一定的性能开销,特别是在遍历对象属性时,可能会影响程序的性能。
  2. 限制功能:Mirror反射API的功能相对有限,只能获取对象的类型信息和属性信息,无法实现更复杂的操作。

Mirror 工作原理

Mirror反射API的工作原理主要包括以下几个步骤:

  1. 获取对象的类型信息:Mirror反射API通过Swift的反射机制获取对象的类型信息,包括类型名称、属性信息等。
  2. 创建Mirror实例:根据类型信息创建Mirror实例,包括Mirror的基本信息和属性信息。
  3. 遍历对象属性:通过Mirror实例遍历对象的属性,获取属性名称和值等信息。
  4. 输出属性信息:将属性信息格式化输出到控制台或日志中,实现自定义的调试输出。

Mirror反射API的工作原理主要依赖于Swift的反射机制,通过反射机制获取对象的类型信息,然后根据类型信息创建Mirror实例。Mirror反射API在Swift中具有广泛的应用场景,特别是在调试和日志输出等方面。

Mirror反射API由Swift实现的ReflectionMirror.swift和C++实现的ReflectionMirror.mm两部分组成。Swift部分主要负责Mirror类的定义和基本功能实现,C++部分则负责底层的反射数据获取和处理。解释两者之间通过@_silgen_name修饰符进行函数映射的通信机制。

Mirror结构体的定义及其初始化方法,包括处理CustomReflectable类型和非CustomReflectable类型。internalReflecting方法包括如何获取subject的真正类型、属性大小、遍历属性并存储到字典中,以及处理父类Mirror的逻辑。_getNormalizedType方法获取传入的subject的真正类型,并调用C++中的swift_reflectionMirror_normalizedType -> Call方法。

ReflectionMirrorImpl的主要种类,如TupleImpl、StructImpl、EnumImpl、ClassImpl、MetatypeImpl、OpaqueImpl等。StructImpl通过metadata获取属性个数、属性名称和值,以及通过指针计算属性存储的地址。

Mirror 代码示例

1
2
3
4
5
6
7
8
9
10
class Movie: NSObject {
var title: String = "Inception"
var duration: Int = 148
}

var movieInstance = Movie()
let movieMirror = Mirror(reflecting: movieInstance)
for attribute in movieMirror.children {
print("\(attribute.label ?? "unknown"): \(attribute.value)")
}

上述代码示例中,定义了一个Movie类,并创建了一个Movie实例movieInstance。通过Mirror反射API,遍历了movieInstance对象的属性,并输出了属性名称和值。

通过Mirror反射API,可以方便地获取对象的类型信息和属性信息,帮助开发者更好地理解和调试代码。

Mirror 与 Runtime

Mirror反射API与Runtime机制在Swift中都是用于处理对象的类型信息和属性信息的技术。Mirror反射API主要用于获取对象的类型信息和属性信息,而Runtime机制主要用于处理对象的生命周期和内存管理。

Mirror反射API通过Swift的反射机制获取对象的类型信息,包括类型名称、属性信息等。通过Mirror反射API,可以动态获取对象的类型信息,遍历对象的属性等操作。

Runtime机制是Swift的底层机制,用于处理对象的生命周期和内存管理。Runtime机制主要包括对象的创建、销毁、内存分配和释放等操作,以及对象的方法调用、属性访问等操作。

Mirror反射API和Runtime机制在Swift中都是用于处理对象的类型信息和属性信息的技有,但功能和应用场景有所不同。Mirror反射API主要用于获取对象的类型信息和属性信息,帮助开发者更好地理解和调试代码;Runtime机制主要用于处理对象的生命周期和内存管理,保证程序的稳定性和性能。

Mirror 与 KVC

Mirror反射API与KVC(Key-Value Coding)在Swift中都是用于处理对象的属性信息的技术。Mirror反射API主要用于获取对象的类型信息和属性信息,而KVC主要用于访问对象的属性值。

Mirror反射API通过Swift的反射机制获取对象的类型信息,包括类型名称、属性信息等。通过Mirror反射API,可以动态获取对象的类型信息,遍历对象的属性等操作。

KVC是一种通过字符串键访问对象属性的技术,可以动态访问对象的属性值。KVC主要包括setValue:forKey:和valueForKey:等方法,用于设置和获取对象的属性值。

Mirror反射API和KVC在Swift中都是用于处理对象的属性信息的技术,但功能和应用场景有所不同。Mirror反射API主要用于获取对象的类型信息和属性信息,帮助开发者更好地理解和调试代码;KVC主要用于访问对象的属性值,实现动态属性访问和赋值等操作。

Mirror 与 Codable

Mirror反射API与Codable协议在Swift中都是用于处理对象的属性信息的技术。Mirror反射API主要用于获取对象的类型信息和属性信息,而Codable协议主要用于对象的编码和解码。

Mirror反射API通过Swift的反射机制获取对象的类型信息,包括类型名称、属性信息等。通过Mirror反射API,可以动态获取对象的类型信息,遍历对象的属性等操作。

Codable协议是Swift的标准库协议,用于对象的编码和解码。Codable协议主要包括Encodable和Decodable两个子协议,用于对象的序列化和反序列化操作。

Mirror反射API和Codable协议在Swift中都是用于处理对象的属性信息的技术,但功能和应用场景有所不同。Mirror反射API主要用于获取对象的类型信息和属性信息,帮助开发者更好地理解和调试代码;Codable协议主要用于对象的编码和解码,实现对象的序列化和反序列化操作。

Mirror 与 PropertyWrapper

Mirror反射API与PropertyWrapper属性包装器在Swift中都是用于处理对象的属性信息的技术。Mirror反射API主要用于获取对象的类型信息和属性信息,而PropertyWrapper属性包装器主要用于属性的封装和访问控制。

Mirror反射API通过Swift的反射机制获取对象的类型信息,包括类型名称、属性信息等。通过Mirror反射API,可以动态获取对象的类型信息,遍历对象的属性等操作。

PropertyWrapper属性包装器是Swift 5.1引入的新特性,用于属性的封装和访问控制。PropertyWrapper属性包装器主要包括@propertyWrapper和wrappedValue等属性,用于属性的封装和访问控制。

Mirror反射API和PropertyWrapper属性包装器在Swift中都是用于处理对象的属性信息的技术,但功能和应用场景有所不同。Mirror反射API主要用于获取对象的类型信息和属性信息,帮助开发者更好地理解和调试代码;PropertyWrapper属性包装器主要用于属性的封装和访问控制,实现属性的封装和访问控制等操作。

动态库注入技术

TrollFools

TrollFools 是一个开源的 iOS 工具,专为 TrollStore 环境设计,用于向 iOS 设备上的应用程序注入和移除插件。

TrollStore 允许用户直接安装 .ipa 文件。这是 TrollFools 能够工作的基础环境。

TrollFools 通过将动态库(dylib)注入到目标应用程序中,来实现对应用程序功能的增强或修改。这些动态库包含了额外的代码,用于在应用程序运行时执行特定的功能。在注入过程中,TrollFools 可能会修改应用程序的二进制文件,以加载并链接这些动态库。这通常涉及到对应用程序的 Mach-O 格式的修改,以确保动态库能够被正确加载和执行。

TrollFools 使用 SwiftUI 编写,这为用户提供了一个现代化的、易于使用的开发环境。开发者可以利用 SwiftUI 的强大功能来创建直观且功能丰富的界面,以进一步扩展 TrollFools 的功能。

由于 optool 存在一些问题,TrollFools 需要编译一个静态链接的 install_name_tool 或 llvm-install-name-tool 以在 iOS 上使用,从而实现更小的包大小。这有助于减少应用占用的存储空间,提高设备的整体性能。