Skip to content

Runtime

给我讲一下 iOS runtime

iOS runtime 是 Apple 提供的一个底层框架,用于支持 Objective-C 语言的动态特性。它是 Objective-C 编译后的核心库,负责实现动态方法调用、消息传递、动态类型检查、动态加载类和对象的内存管理等功能。了解 iOS runtime 对于深入理解和优化 iOS 应用程序至关重要。以下是 iOS runtime 的一些关键概念和功能:

1. 动态类型与消息传递

在 Objective-C 中,方法调用被称为“消息传递”。每个方法调用实际上是向对象发送一条消息。例如,调用 [object doSomething] 实际上是发送消息 doSomethingobject。这种机制使得程序在运行时可以决定调用哪个方法。

2. 方法与消息

Objective-C 的方法调用是通过消息传递机制实现的,objc_msgSend 是最常用的消息传递函数。方法的具体实现被动态地绑定到消息上。方法选择器(selector)是方法名的唯一标识符,通常是 SEL 类型。

3. 动态方法解析

当一个对象接收到一个无法识别的消息时,runtime 会提供机会动态解析这个方法。可以通过实现 +(BOOL)resolveInstanceMethod:(SEL)sel+(BOOL)resolveClassMethod:(SEL)sel 来动态添加方法实现。

4. 消息转发

如果一个对象无法处理某个消息,可以通过消息转发机制将消息转发给其他对象。实现 - (id)forwardingTargetForSelector:(SEL)aSelector- (void)forwardInvocation:(NSInvocation *)anInvocation 方法可以处理无法识别的消息。

5. 类与元类

在 runtime 中,每个类(class)本身也是一个对象。每个类都有一个指向其元类(metaclass)的指针,元类定义了类方法的实现。实例方法保存在类对象中,类方法保存在元类对象中。

6. 动态特性

Objective-C 允许在运行时动态创建类、添加方法、交换方法实现等。这使得开发者可以实现一些灵活的功能,如方法调配(method swizzling)和动态代理(dynamic proxies)。

7. 内存管理

iOS runtime 负责对象的内存管理,包括引用计数和自动释放池(autorelease pool)的实现。ARC(Automatic Reference Counting)是编译器特性,但背后依赖 runtime 提供的内存管理功能。

8. Associated Objects

可以使用 runtime 动态地将属性关联到对象上。这是通过 objc_setAssociatedObjectobjc_getAssociatedObject 函数实现的。它使得开发者可以在不修改类定义的情况下,向对象添加新的属性。

常用的 runtime 函数

  • class_getName:获取类名
  • class_getInstanceMethod:获取实例方法
  • class_getClassMethod:获取类方法
  • method_getImplementation:获取方法的实现
  • method_setImplementation:设置方法的实现
  • objc_allocateClassPair:创建新类
  • objc_registerClassPair:注册新创建的类

示例代码

以下是一个简单示例,展示了如何使用 runtime 动态添加方法:

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"This is a dynamically added method.");
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 动态创建一个类
        Class DynamicClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);

        // 动态添加方法
        class_addMethod(DynamicClass, @selector(dynamicMethod), (IMP)dynamicMethodIMP, "v@:");

        // 注册类
        objc_registerClassPair(DynamicClass);

        // 创建类实例
        id instance = [[DynamicClass alloc] init];

        // 调用动态添加的方法
        [instance performSelector:@selector(dynamicMethod)];
    }
    return 0;
}

这个示例展示了如何动态创建一个类并向其添加方法,以及如何调用这个动态添加的方法。

iOS runtime 提供了强大的动态特性,使得 Objective-C 语言能够灵活高效地实现各种功能。理解并善用这些特性,对于开发高效、灵活的 iOS 应用至关重要。


objc_object, objc_class, isa 指针,method_t 这些都是什么,解释一下,还有实际用处

在 Objective-C 的 runtime 系统中,objc_objectobjc_classisa 指针和 method_t 是一些核心概念和结构体,它们在实现动态特性和面向对象机制中起着重要作用。下面分别解释这些概念及其实际用处。

1. objc_object

objc_object 是 Objective-C 中所有对象的基类,实际上是一个包含 isa 指针的结构体。这个 isa 指针指向对象的类。

struct objc_object {
    Class isa;
};

2. objc_class

objc_class 是描述类的结构体。它本质上也是一个 objc_object,因此包含 isa 指针。一个类的 isa 指针指向它的元类(metaclass)。

struct objc_class {
    Class isa;  // 指向元类
    Class super_class;  // 指向父类
    const char *name;  // 类名
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;  // 成员变量列表
    struct objc_method_list **methodLists;  // 方法列表
    struct objc_cache *cache;  // 方法缓存
    struct objc_protocol_list *protocols;  // 协议列表
};

3. isa 指针

isa 指针是每个 objc_objectobjc_class 都包含的一个指针。它是 runtime 系统中用于实现动态类型和消息传递机制的关键。

  • 对于实例对象,isa 指针指向它的类(objc_class)。
  • 对于类对象,isa 指针指向它的元类(metaclass)。
  • 元类的 isa 指针最终指向 NSObject 的元类,形成一个闭环。

isa 指针的作用包括: - 用于确定对象的类型。 - 用于查找对象的方法实现。

4. method_t

method_t 是一个描述方法的结构体,包含方法的选择器(selector)、方法实现(IMP)和方法类型编码(type encoding)。

struct method_t {
    SEL name;  // 方法选择器
    const char *types;  // 方法类型编码
    IMP imp;  // 方法实现
};

实际用处

理解这些结构体和指针在实际开发中有多种用处:

动态类型与消息传递

isa 指针是实现动态类型检查和消息传递的关键。当一个对象接收到消息时,runtime 系统通过 isa 指针找到对象的类,然后在类的方法列表中查找对应的 method_t 结构体,找到方法实现并调用。

方法调配(Method Swizzling)

开发者可以使用 runtime 方法交换技术(method swizzling)来替换类的方法实现,从而在不修改原始代码的情况下添加或修改功能。这在调试、日志记录、AOP(面向切面编程)等方面非常有用。

void MethodSwizzle(Class c, SEL origSEL, SEL newSEL) {
    Method origMethod = class_getInstanceMethod(c, origSEL);
    Method newMethod = class_getInstanceMethod(c, newSEL);
    method_exchangeImplementations(origMethod, newMethod);
}

动态添加方法

开发者可以在运行时动态向类添加方法,增强类的功能。例如,可以通过 class_addMethod 函数添加新的方法。

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);

动态创建类

可以在运行时动态创建新的类,满足某些动态需求。例如,根据不同的运行时环境创建不同的类实现。

Class newClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);
objc_registerClassPair(newClass);

Associated Objects

通过 objc_setAssociatedObjectobjc_getAssociatedObject 函数,开发者可以动态地将任意数据关联到现有对象上,扩展对象的功能而无需修改类定义。

objc_setAssociatedObject(object, @selector(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
id value = objc_getAssociatedObject(object, @selector(property));

总结

了解 objc_objectobjc_classisa 指针和 method_t 结构体对深入理解 iOS runtime 系统至关重要。这些概念帮助开发者掌握动态类型、消息传递、方法调配等高级特性,使得开发者能够创建更灵活和强大的 iOS 应用程序。


对象的 isa 指向类,类的 isa 指向元类? 类中存放对象的方法,也就是那些减号方法,元类中存放类的方法 也就是那些加号方法,理解的对么

是的,你的理解是正确的。下面我详细解释一下这一点:

1. isa 指针的层次结构

  • 对象(Instance Object)
  • 对象的 isa 指针指向它所属的类(objc_class)。

  • 类(Class)

  • 类的 isa 指针指向元类(Metaclass)。
  • 类中存储实例方法(即以 - 开头的方法)。

  • 元类(Metaclass)

  • 元类的 isa 指针指向根元类(Root Metaclass),通常是 NSObject 的元类。
  • 元类中存储类方法(即以 + 开头的方法)。

  • 根元类(Root Metaclass)

  • 根元类的 isa 指针指向自身,形成一个闭环。

2. 层次结构图示

                    +----------------------+
                    |  Root Metaclass      |
                    +----------------------+
                    |  +classMethod        |
       isa  +-------------------------------+  isa
       +----> | isa指向自身                  |
       |    +-------------------------------+
       |
  +----v----------------+
  |  Metaclass          |
  +---------------------+
  |  +classMethod       |
  +---------------------+
       isa  +-------------------------------+  isa
       +----> | isa指向Root Metaclass         |
       |    +-------------------------------+
       |
  +----v----------------+
  |  Class              |
  +---------------------+
  |  -instanceMethod    |
  +---------------------+
       isa  +-------------------------------+  isa
       +----> | isa指向Metaclass              |
            +-------------------------------+
                 |
  +--------------v--------------+
  |  Instance Object            |
  +-----------------------------+
  |  isa指向Class               |
  +-----------------------------+

3. 方法存储

  • 实例方法(Instance Methods)
  • 定义在类对象中,以 - 开头。
  • 这些方法是实例对象可以调用的方法。
  • 例如,- (void)instanceMethod

  • 类方法(Class Methods)

  • 定义在元类对象中,以 + 开头。
  • 这些方法是类本身可以调用的方法。
  • 例如,+ (void)classMethod

实际例子

假设有一个类 MyClass,并且它有一个实例方法 - (void)instanceMethod 和一个类方法 + (void)classMethod

  • 实例对象
  • MyClass *myObject = [[MyClass alloc] init];
  • myObjectisa 指针指向 MyClass

  • 类对象

  • MyClassisa 指针指向它的元类 MyClass 的元类。

  • 元类对象

  • MyClass 的元类的 isa 指针指向根元类 NSObject 的元类。

  • 方法存储

  • MyClass 中存储了 - (void)instanceMethod,所以 myObject 可以调用 [myObject instanceMethod]
  • MyClass 的元类中存储了 + (void)classMethod,所以 MyClass 可以调用 [MyClass classMethod]

总结

你的理解是正确的,实例方法存储在类中,类方法存储在元类中。isa 指针层次结构确保了在运行时能够正确地找到并调用这些方法。这种设计使得 Objective-C 的动态特性和消息传递机制能够有效运作。


id = objc_object 吗

在 Objective-C 中,id 是一种特殊的类型,它可以表示任何类型的对象。尽管在底层实现中 id 的确是与 objc_object * 相关联的,但它们并不完全相同。

id 类型

  • 定义id 是一种通用的对象指针类型。它可以指向任何 Objective-C 对象。
  • 特点id 类型的变量是动态类型的,也就是说,编译器不会检查 id 类型变量所指向对象的具体类型。可以调用任何对象的方法,具体的类型检查和方法调用在运行时进行。

objc_object 结构体

  • 定义objc_object 是 Objective-C runtime 用来表示对象的结构体。
  • 结构objc_object 结构体至少包含一个指向类的 isa 指针。实际上,Objective-C 的每个对象在底层都是 objc_object 类型的。
struct objc_object {
    Class isa;
};

idobjc_object * 的关系

  • 在底层,id 实际上是一个指向 objc_object 结构体的指针,也就是说 id 可以被看作 objc_object * 类型。
  • 由于 id 是通用的对象类型,它可以表示任何继承自 objc_object 的对象,包括自定义的 Objective-C 类实例。

示例

id obj = [[NSObject alloc] init];

在这段代码中,obj 是一个 id 类型的变量,它指向一个 NSObject 的实例。底层实现中,这个 NSObject 实例是一个 objc_object 结构体,并且 obj 实际上是一个指向 objc_object 结构体的指针。

小结

  • id 是一个可以表示任何 Objective-C 对象的通用指针类型。
  • objc_object 是 Objective-C runtime 用来表示对象的底层结构体。
  • 从技术角度看,id 可以被认为是 objc_object * 类型,但它提供了更多的灵活性,因为它可以指向任何类型的对象而无需编译时类型检查。

这种设计使得 Objective-C 具有高度的动态性和灵活性,能够在运行时处理多态对象和消息传递。


我有个问题,你说 objc_object 是 描述对象的结构体,objc_class 是描述类的结构体,对象不是类生成的么,为啥 objc_class 要继承 objc_object 呢?

这是一个非常好的问题,涉及到 Objective-C runtime 系统的设计原理。在 Objective-C 中,对象(instance)、类(class)和元类(metaclass)之间的关系和继承层次有一定的复杂性,这样设计是为了实现面向对象的动态特性和消息传递机制。

对象、类和元类的关系

  1. 对象(Instance)
  2. 由类生成,每个对象都包含一个 isa 指针,指向它的类。
  3. isa 指针用于在运行时确定对象的类型和查找方法的实现。

  4. 类(Class)

  5. 描述一组对象的结构和行为。
  6. 类本身也是一个对象,因此需要一个 isa 指针,指向它的元类。
  7. 类中存储了实例方法、成员变量等信息。

  8. 元类(Metaclass)

  9. 描述类的类,也就是类的结构和行为。
  10. 元类本身也是一个对象,因此也需要一个 isa 指针,指向根元类。
  11. 元类中存储了类方法。

为什么 objc_class 继承 objc_object

在 Objective-C 中,类和元类也是对象。为了使类和元类能够参与运行时消息传递机制和动态特性,它们必须具有对象的基本特性。这就是为什么 objc_class 继承自 objc_object。具体来说:

  1. 统一的对象模型
  2. 在 Objective-C 中,一切都是对象。这包括实例、类和元类。通过让类和元类继承自 objc_object,确保它们都具有一个 isa 指针,从而可以使用相同的机制进行类型判断和消息传递。

  3. 消息传递机制

  4. 消息传递是 Objective-C 的核心机制。对象通过 isa 指针找到它的类,然后在类的方法列表中查找方法实现。同样,类方法的查找需要通过类的 isa 指针找到元类,再在元类的方法列表中查找方法实现。

继承层次

                  +----------------------+
                  |  Root Metaclass      |
                  +----------------------+
                      isa ^       |
                          |       |
                  +-------+-------+
                  |  Metaclass          |
                  +----------------------+
                      isa ^       |
                          |       |
                  +-------+-------+
                  |  Class              |
                  +----------------------+
                      isa ^       |
                          |       |
                  +-------+-------+
                  |  Instance Object    |
                  +----------------------+

具体结构

  • objc_objectobjective-c struct objc_object { Class isa; };

  • objc_class(继承自 objc_object): objective-c struct objc_class : objc_object { Class super_class; // 指向父类 const char *name; // 类名 long version; long info; long instance_size; struct objc_ivar_list *ivars; // 成员变量列表 struct objc_method_list **methodLists; // 方法列表 struct objc_cache *cache; // 方法缓存 struct objc_protocol_list *protocols; // 协议列表 };

总结

  • objc_object 是所有对象的基类,包括实例对象、类对象和元类对象。
  • 通过让 objc_class 继承自 objc_object,确保类和元类也具有对象的特性,可以参与消息传递和动态类型检查。
  • 这种设计使得 Objective-C runtime 能够统一处理实例、类和元类,实现灵活的面向对象机制。

通过这种设计,Objective-C 能够提供强大的动态特性,支持灵活的消息传递和运行时类型处理。


objc_object 是 Objective-C 中所有对象的基类,实际上是一个包含 isa 指针的结构体。这个 isa 指针指向对象的类。

这句话 所有对象只的是什么对象,是一个类 alloc init 出来的那个对象么. 你说是所有对象的基类,那 nsobject 是啥

你的问题很有意义,可以帮助深入理解 Objective-C 的对象模型。让我们一起来理清几个概念:

objc_objectNSObject 的区别

  1. objc_object
  2. 定义objc_object 是 Objective-C runtime 系统中的基础结构体,它定义了所有 Objective-C 对象的基本结构。
  3. 内容:这个结构体包含一个 isa 指针,用于指向对象的类。
  4. 作用:它是 runtime 层面上的基类,所有的 Objective-C 对象在底层都可以被视为 objc_object 类型。
struct objc_object {
    Class isa;
};
  1. NSObject
  2. 定义NSObject 是一个实际存在于代码中的基类,绝大多数 Objective-C 类都是直接或间接继承自 NSObject
  3. 作用:它提供了许多基础功能和方法,如对象的初始化、内存管理、方法调用等。
@interface NSObject
// 基本方法和属性定义
@end

所有对象的基类

当我们说 objc_object 是所有对象的基类时,这里的“对象”指的是所有通过 allocinit 方法创建出来的实例对象。这些实例对象在底层都是由 objc_object 结构体表示的。

isa 指针的作用

每个 objc_object 都有一个 isa 指针,指向它所属的类。通过 isa 指针,runtime 可以在运行时确定对象的类型,并找到相应的方法实现。

NSObjectobjc_object 的关系

NSObject 是一个实际存在的类,定义了许多常用的方法和属性。NSObject 在 runtime 层面上可以看作是 objc_object 的一种扩展。换句话说,NSObject 继承自 objc_object,虽然这种继承关系在实际代码中不是直接体现的,而是由 Objective-C runtime 系统隐式处理的。

实例对象、类对象和元类对象

  1. 实例对象
  2. 通过 allocinit 创建的实际对象。
  3. 例如,MyClass *obj = [[MyClass alloc] init];
  4. obj 是一个实例对象,底层结构是 objc_object,其 isa 指针指向 MyClass

  5. 类对象

  6. 定义实例对象行为的对象。
  7. 例如,[MyClass class] 返回 MyClass 的类对象,其 isa 指针指向 MyClass 的元类。

  8. 元类对象

  9. 定义类对象行为的对象。
  10. 例如,MyClass 的元类,其 isa 指针指向根元类(通常是 NSObject 的元类)。

总结

  • objc_object 是一个底层结构体,所有的 Objective-C 对象在底层都是 objc_object 类型。
  • NSObject 是一个实际的基类,大多数 Objective-C 类都继承自它,提供了许多基础功能。
  • 当我们说 objc_object 是所有对象的基类时,是指所有通过 allocinit 方法创建出来的实例对象在底层都可以被看作 objc_object 类型。
  • isa 指针在对象、类和元类之间建立了关系,使得 Objective-C 能够实现动态消息传递和类型判断。

通过理解这些概念,我们可以更深入地理解 Objective-C 的对象模型和运行机制。


当我们说 objc_object 是所有对象的基类时,是指所有通过 alloc 和 init 方法创建出来的实例对象在底层都可以被看作 objc_object 类型。

这句话,和你上面说的一句话 objc_object 是所有对象的基类,包括实例对象、类对象和元类对象。 类对象通过什么方法获得的,元类对象呢

你提出的问题非常关键,有助于深入理解 Objective-C 的对象模型及其运行时机制。为了进一步澄清,我们需要更详细地探讨类对象和元类对象的获取方式,以及它们在 Objective-C runtime 系统中的地位和关系。

1. objc_object 是所有对象的基类

objc_object 是一个底层结构体,是 Objective-C runtime 中所有对象的基类。这包括实例对象、类对象和元类对象。在更具体的层次上:

  • 实例对象(Instance Object):通过 allocinit 方法创建。
  • 类对象(Class Object):表示类本身,是通过消息传递或 runtime 函数获取。
  • 元类对象(Metaclass Object):表示类的类,用于存储类方法,同样可以通过 runtime 函数获取。

2. 实例对象、类对象和元类对象的获取方式

实例对象的创建

实例对象是通过类的 allocinit 方法创建的。

MyClass *instance = [[MyClass alloc] init];

在这个例子中,instance 是一个 MyClass 的实例对象,其 isa 指针指向 MyClass

类对象的获取

类对象表示类本身,可以通过 [MyClass class] 或 runtime 提供的函数 objc_getClass 获取。

Class classObject = [MyClass class];
// 或者
Class classObject = objc_getClass("MyClass");

在这个例子中,classObjectMyClass 的类对象,其 isa 指针指向 MyClass 的元类。

元类对象的获取

元类对象表示类的类,可以通过 runtime 提供的函数 object_getClass 获取。

Class metaclassObject = object_getClass([MyClass class]);

在这个例子中,metaclassObjectMyClass 的元类对象,其 isa 指针指向根元类(通常是 NSObject 的元类)。

3. 类与元类的层次结构

在 Objective-C 中,类对象和元类对象之间有一个层次结构,用于实现消息传递和动态类型检查。以下是一个简单的层次结构图:

                  +----------------------+
                  |  Root Metaclass      |
                  +----------------------+
                          ^
                          |
                  +-------+-------+
                  |  Metaclass          |
                  +----------------------+
                          ^
                          |
                  +-------+-------+
                  |      Class          |
                  +----------------------+
                          ^
                          |
                  +-------+-------+
                  |  Instance Object    |
                  +----------------------+
  • 实例对象isa 指针指向它的类对象。
  • 类对象isa 指针指向它的元类对象。
  • 元类对象isa 指针指向根元类对象(NSObject 的元类),形成一个闭环。

4. 示例代码

以下代码展示了如何获取类对象和元类对象:

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建实例对象
        MyClass *instance = [[MyClass alloc] init];

        // 获取类对象
        Class classObject = [MyClass class];

        // 获取元类对象
        Class metaclassObject = object_getClass(classObject);

        // 打印结果
        NSLog(@"Instance isa: %s", class_getName(object_getClass(instance)));
        NSLog(@"Class isa: %s", class_getName(object_getClass(classObject)));
        NSLog(@"Metaclass isa: %s", class_getName(object_getClass(metaclassObject)));
    }
    return 0;
}

运行结果大致如下:

Instance isa: MyClass
Class isa: MyClass (metaclass)
Metaclass isa: NSObject (metaclass)

总结

  • objc_object 是所有对象的基类,包括实例对象、类对象和元类对象。
  • 实例对象通过 allocinit 方法创建。
  • 类对象通过 [MyClass class]objc_getClass 函数获取。
  • 元类对象通过 object_getClass 函数获取。

理解这些关系和获取方式,有助于更深入地掌握 Objective-C 的运行时机制和面向对象编程模型。


你这个代码例子 对象的 isa 指向的 myclass, myclass 的 isa 指向他的元类,他的元类名字为啥 也叫 myclass

你提到的疑问是关于为什么在日志中打印出的元类的名称也是 MyClass。这是因为 object_getClass 返回的对象的名称会根据 class_getName 方法返回类的名称,而元类的名称与其对应的类是相同的。

解释 isa 指针和类名称

每个实例对象都有一个 isa 指针指向其所属的类对象。类似地,每个类对象也有一个 isa 指针指向其元类对象。元类对象的名称与它所表示的类是相同的,这样设计是为了简化和统一类和元类的处理。

class_getName 函数的作用

class_getName 函数用于获取类的名称,无论它是一个类还是元类,都会返回相同的名称。为了进一步澄清这个问题,我们可以调整代码示例,明确区分类对象和元类对象。

详细示例代码

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建实例对象
        MyClass *instance = [[MyClass alloc] init];

        // 获取类对象
        Class classObject = [MyClass class];

        // 获取元类对象
        Class metaclassObject = object_getClass(classObject);

        // 获取根元类对象
        Class rootMetaclassObject = object_getClass(metaclassObject);

        // 打印结果
        NSLog(@"Instance isa: %s", class_getName(object_getClass(instance)));
        NSLog(@"Class name: %s", class_getName(classObject));
        NSLog(@"Class isa (Metaclass): %s", class_getName(metaclassObject));
        NSLog(@"Metaclass isa (Root Metaclass): %s", class_getName(rootMetaclassObject));
    }
    return 0;
}

结果分析

在这段代码中,我们打印了以下信息:

  1. 实例对象的 isa 指针
  2. 指向 MyClass 类。
  3. 输出:Instance isa: MyClass

  4. 类对象的名称

  5. 直接获取类对象的名称。
  6. 输出:Class name: MyClass

  7. 类对象的 isa 指针(指向元类对象):

  8. 获取 MyClass 的元类对象。
  9. 元类对象的名称与其对应的类相同。
  10. 输出:Class isa (Metaclass): MyClass

  11. 元类对象的 isa 指针(指向根元类对象):

  12. 获取 MyClass 元类对象的 isa 指针,通常是指向 NSObject 的元类。
  13. 输出:Metaclass isa (Root Metaclass): NSObject

总结

  • 类对象和元类对象:在 Objective-C 中,类对象和元类对象的名称是相同的。这是为了统一处理类和元类。
  • isa 指针:通过 isa 指针,实例对象指向其类对象,类对象指向其元类对象,而元类对象通常指向根元类(如 NSObject 的元类)。

这样设计使得 Objective-C 运行时能够统一管理对象、类和元类,简化消息传递和类型判断的实现。


好了 我来总结一下,看我说的哪里有问题。 手相 objc_object 是一些对象的基类,包括实例对象,类对象,元类对象。这里所说的基类是继承概念吗,我觉得不是。objc_object 是结构体,是描述所有对象的结构体。objc_class 呢 他是描述类的结构体,同时也继承 objc_object,,对象,类,元类,他们之间靠什么联系呢,就是 这个 isa 指针,这个指针在哪里呢,就在 objc_object 结构体中。

你的总结基本上是正确的,不过有一些细节可以进一步澄清和完善。以下是对你的总结的详细解释和补充:

1. objc_object 是所有对象的基类

  • 准确性objc_object 并不是一个基类,而是一个基础的结构体,用于描述所有对象的结构。它包含一个 isa 指针,这个指针指向对象的类。
  • 继承关系objc_object 并不是通过继承的概念来实现的,而是通过结构体嵌套来实现的。objc_object 是所有 Objective-C 对象的基础结构。
struct objc_object {
    Class isa;
};

2. objc_class 继承自 objc_object

  • 准确性objc_class 是一个结构体,用于描述类的结构。它实际上是从 objc_object 继承而来的,这意味着 objc_class 也是一种特殊的 objc_object,因此它也包含一个 isa 指针。
struct objc_class : objc_object {
    Class super_class; // 指向父类
    const char *name;  // 类名
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;  // 成员变量列表
    struct objc_method_list **methodLists;  // 方法列表
    struct objc_cache *cache;  // 方法缓存
    struct objc_protocol_list *protocols;  // 协议列表
};

3. isa 指针的作用

  • 准确性isa 指针在 objc_object 结构体中,用于连接对象、类和元类之间的关系。通过 isa 指针,实例对象可以找到它的类对象,类对象可以找到它的元类对象。

4. 实例对象、类对象和元类对象的关系

  • 实例对象:通过 allocinit 方法创建,每个实例对象的 isa 指针指向它的类。
  • 类对象:描述一类实例对象的结构和行为。类对象的 isa 指针指向它的元类。
  • 元类对象:描述类对象的结构和行为。元类对象的 isa 指针通常指向根元类(如 NSObject 的元类)。

示例结构

                    +----------------------+
                    |  Root Metaclass      |
                    +----------------------+
                        isa ^       |
                            |       |
                    +-------+-------+
                    |  Metaclass          |
                    +----------------------+
                        isa ^       |
                            |       |
                    +-------+-------+
                    |      Class          |
                    +----------------------+
                        isa ^       |
                            |       |
                    +-------+-------+
                    |  Instance Object    |
                    +----------------------+

示例代码

以下代码展示了如何获取实例对象、类对象和元类对象,并打印它们的关系:

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建实例对象
        MyClass *instance = [[MyClass alloc] init];

        // 获取类对象
        Class classObject = [MyClass class];

        // 获取元类对象
        Class metaclassObject = object_getClass(classObject);

        // 获取根元类对象
        Class rootMetaclassObject = object_getClass(metaclassObject);

        // 打印结果
        NSLog(@"Instance isa: %s", class_getName(object_getClass(instance)));
        NSLog(@"Class name: %s", class_getName(classObject));
        NSLog(@"Class isa (Metaclass): %s", class_getName(metaclassObject));
        NSLog(@"Metaclass isa (Root Metaclass): %s", class_getName(rootMetaclassObject));
    }
    return 0;
}

进一步澄清

  • objc_object:是所有对象的基础结构体,包括实例对象、类对象和元类对象。它通过 isa 指针连接这些对象。
  • objc_class:是描述类的结构体,继承自 objc_object,因此它也是一个 objc_objectobjc_class 中包含了描述类的其他信息,如方法列表、成员变量等。

总结

  • 对象、类和元类:它们之间的关系是通过 isa 指针连接的。每个 objc_object 都有一个 isa 指针指向它的类,类对象的 isa 指针指向它的元类,元类对象的 isa 指针通常指向根元类。
  • isa 指针的位置isa 指针位于 objc_object 结构体中,是对象与类、类与元类之间的连接纽带。

你的理解基本是正确的,只是需要明确 objc_object 是基础结构体,而非传统意义上的基类。通过这种设计,Objective-C 能够实现其动态特性和灵活的消息传递机制。


objc_object 是 Objective-C 中所有对象的基类,实际上是一个包含 isa 指针的结构体。这个 isa 指针指向对象的类。

这是你之间说的,你觉得有没有歧义

你提到的句子确实可能引起一些歧义。特别是“基类”这个术语在上下文中的使用可能会让人误解。更准确地描述应该强调 objc_object 是一个基础结构体,而不是传统意义上的基类。我们可以这样改进:

原句

objc_object 是 Objective-C 中所有对象的基类,实际上是一个包含 isa 指针的结构体。这个 isa 指针指向对象的类。

改进后的描述

objc_object 是 Objective-C 中所有对象的基础结构体。它包含一个 isa 指针,该指针指向对象的类。

详细解释

  • 基础结构体objc_object 是 Objective-C 运行时系统中描述所有对象的最基础的结构体。它并不是一个类,而是一个结构体定义。
  • isa 指针isa 指针是 objc_object 结构体中的一个成员,它指向对象所属的类。

整理后的总结

// 定义 `objc_object` 结构体
struct objc_object {
    Class isa;
};

// 定义 `objc_class` 结构体,继承自 `objc_object`
struct objc_class : objc_object {
    Class super_class; // 指向父类
    const char *name;  // 类名
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;  // 成员变量列表
    struct objc_method_list **methodLists;  // 方法列表
    struct objc_cache *cache;  // 方法缓存
    struct objc_protocol_list *protocols;  // 协议列表
};

关键点

  1. objc_object:所有 Objective-C 对象的基础结构体,包含一个 isa 指针。
  2. objc_class:继承自 objc_object,描述类的结构体,包含类的详细信息(如方法列表、成员变量等)。
  3. isa 指针:用于将实例对象与其类对象、类对象与其元类对象连接起来。

结构图

                    +----------------------+
                    |  Root Metaclass      |
                    +----------------------+
                        isa ^       |
                            |       |
                    +-------+-------+
                    |  Metaclass          |
                    +----------------------+
                        isa ^       |
                            |       |
                    +-------+-------+
                    |      Class          |
                    +----------------------+
                        isa ^       |
                            |       |
                    +-------+-------+
                    |  Instance Object    |
                    +----------------------+

示例代码

以下代码展示了如何获取实例对象、类对象和元类对象,并打印它们的关系:

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建实例对象
        MyClass *instance = [[MyClass alloc] init];

        // 获取类对象
        Class classObject = [MyClass class];

        // 获取元类对象
        Class metaclassObject = object_getClass(classObject);

        // 获取根元类对象
        Class rootMetaclassObject = object_getClass(metaclassObject);

        // 打印结果
        NSLog(@"Instance isa: %s", class_getName(object_getClass(instance)));
        NSLog(@"Class name: %s", class_getName(classObject));
        NSLog(@"Class isa (Metaclass): %s", class_getName(metaclassObject));
        NSLog(@"Metaclass isa (Root Metaclass): %s", class_getName(rootMetaclassObject));
    }
    return 0;
}

总结

  • objc_object 是所有 Objective-C 对象的基础结构体,而不是基类。
  • 该结构体包含一个 isa 指针,用于连接对象与类、类与元类之间的关系。
  • objc_class 是一个继承自 objc_object 的结构体,描述类的详细信息。

这样改写可以避免歧义,更准确地描述 objc_objectobjc_class 在 Objective-C 运行时系统中的角色。


cache_t, class_data_bits_t,class_rw_t,class_ro_t,method_t 给我解释一下这些都是干啥玩意的

在 Objective-C 的运行时(runtime)系统中,这些结构体和类型负责管理类和方法的信息。我们来详细解释一下 cache_tclass_data_bits_tclass_rw_tclass_ro_tmethod_t 这些类型的作用。

1. cache_t

cache_t 是用于方法缓存的结构体。它在类的结构体中起到了加速方法查找的作用。当一个方法被调用时,runtime 系统首先会在这个缓存中查找,以减少方法查找的开销。

struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
};
  • _buckets:缓存桶数组,存储方法的缓存条目。
  • _mask:用于计算索引的掩码。
  • _occupied:已使用的缓存条目的数量。

2. class_data_bits_t

class_data_bits_t 是一个位域,包含类的只读和读写数据的指针。在 Apple 的运行时实现中,这个类型用于高效存储和访问类的数据。

typedef struct {
    uintptr_t bits;
} class_data_bits_t;
  • bits:存储类的数据指针,包括只读和读写数据。

3. class_rw_t

class_rw_t 是类的读写数据的结构体,包含类的动态信息,如方法列表、属性列表等。这些数据在运行时可以被修改。

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;  // 指向只读数据
    method_array_t methods;  // 方法列表
    property_array_t properties;  // 属性列表
    protocol_array_t protocols;  // 协议列表
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};
  • flags:标志位,描述类的状态。
  • version:版本号。
  • ro:指向只读数据的指针。
  • methods:方法列表。
  • properties:属性列表。
  • protocols:协议列表。
  • firstSubclass:第一个子类。
  • nextSiblingClass:下一个兄弟类。
  • demangledName:解混淆后的类名。

4. class_ro_t

class_ro_t 是类的只读数据的结构体,包含类的静态信息,如类名、实例大小、成员变量列表等。这些数据在运行时不可修改。

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    const uint8_t * ivarLayout;
    const char *name;  // 类名
    const method_list_t *baseMethods;  // 基本方法列表
    const protocol_list_t *baseProtocols;  // 基本协议列表
    const ivar_list_t *ivars;  // 成员变量列表
    const uint8_t *weakIvarLayout;
    const property_list_t *properties;  // 属性列表
};
  • flags:标志位,描述类的状态。
  • instanceStart:实例变量起始位置。
  • instanceSize:实例大小。
  • ivarLayout:实例变量布局。
  • name:类名。
  • baseMethods:基本方法列表。
  • baseProtocols:基本协议列表。
  • ivars:成员变量列表。
  • weakIvarLayout:弱引用实例变量布局。
  • properties:属性列表。

5. method_t

method_t 是表示方法的结构体,包含方法的相关信息。

struct method_t {
    SEL name;  // 方法选择器
    const char *types;  // 方法类型编码
    IMP imp;  // 方法实现的指针
};
  • name:方法选择器(方法的名称)。
  • types:方法类型编码(描述方法参数和返回类型的编码)。
  • imp:方法实现的指针(指向方法的实现函数)。

结构和关系

这些结构体和类型在 Objective-C 的类和方法管理中起着关键作用。它们的关系和作用可以通过下面的示例代码来理解:

// 类对象结构示例
struct objc_class {
    Class isa;
    Class super_class;
    cache_t cache;  // 方法缓存
    class_data_bits_t bits;  // 类数据位域
};

// 类数据位域展开后的数据
struct class_data_t {
    class_ro_t *ro;  // 只读数据
    class_rw_t *rw;  // 读写数据
};

// 读写数据包含的方法列表
struct class_rw_t {
    method_array_t methods;  // 方法列表
    // 其他字段省略
};

// 方法列表包含的方法
struct method_list_t {
    uint32_t count;
    method_t first;
};

总结

  • cache_t:用于方法缓存,加速方法查找。
  • class_data_bits_t:包含类的只读和读写数据指针的位域。
  • class_rw_t:类的读写数据,包含方法列表、属性列表等,可以在运行时修改。
  • class_ro_t:类的只读数据,包含类名、实例大小、成员变量列表等,运行时不可修改。
  • method_t:表示方法的结构体,包含方法选择器、类型编码和实现指针。

这些结构体和类型共同构成了 Objective-C 运行时系统中的类和方法管理机制,使得类的定义、方法的查找和调用等操作得以高效执行。


isa_t 是啥 啥叫指针类型的 isa 和非指针类型的 isa

isa_t 是一个在 Objective-C 运行时中用来表示 isa 指针的结构体。它包含了指向对象的类的指针以及一些标志位,用于存储对象的附加信息。在现代的 Objective-C 运行时中,Apple 引入了“优化的 isa”技术,这使得 isa 指针不仅仅是一个简单的指针,而是一个多用途的结构,包含多个字段。

指针类型的 isa 和非指针类型的 isa

  • 指针类型的 isa:传统的 isa 指针是一个简单的指针,直接指向对象的类。
  • 非指针类型的 isa:优化的 isa 是一个包含多个字段的位域(bitfield),这些字段可以包含指向类的指针以及其他标志位和信息。

isa_t 结构体

在优化的 isa 实现中,isa 被定义为一个联合体,包含一个指针和一个位域。以下是 isa_t 的一个示例定义(简化版):

union isa_t {
    Class cls;
    uintptr_t bits;
};

struct obj {
    isa_t isa;
};

优化的 isa 位域

在优化的 isa 实现中,isa_t 包含多个字段,例如:

  • 类指针(class pointer):指向对象所属的类。
  • 指针标志位(pointer bit):指示 isa 是否为指针类型。
  • 弱引用计数(weak reference count):管理对象的弱引用计数。
  • 额外标志位(extra flags):存储附加信息,如对象是否已被标记为自由等。

以下是一个更详细的示例:

union isa_t {
    Class cls;
    uintptr_t bits;
    struct {
        uintptr_t nonpointer : 1;  // 指示是否为优化的 isa
        uintptr_t has_assoc : 1;   // 是否有关联对象
        uintptr_t has_cxx_dtor : 1; // 是否有 C++ 析构函数
        uintptr_t shiftcls : 33;   // 类指针,偏移量
        uintptr_t magic : 6;       // 用于调试的魔术数
        uintptr_t weakly_referenced : 1; // 是否有弱引用
        uintptr_t deallocating : 1; // 是否正在析构
        uintptr_t has_sidetable_rc : 1; // 是否有 side table 引用计数
        uintptr_t extra_rc : 19;   // 引用计数
    };
};

指针类型的 isa 示例

// 指针类型的 isa
struct objc_object {
    Class isa;
};

非指针类型的 isa 示例

// 非指针类型的 isa
struct objc_object {
    union isa_t isa;
};

使用优化 isa 的好处

  • 减少内存使用:通过在 isa 指针中存储额外的信息,可以减少对象中额外字段的数量。
  • 提高性能:某些标志位和计数可以直接在 isa 中存储和访问,而无需额外的查找和操作。

例子:如何访问优化的 isa

以下是如何访问和使用优化的 isa 的示例代码:

struct objc_object {
    union isa_t isa;
};

// 获取类指针
Class get_class(struct objc_object *obj) {
    if (obj->isa.nonpointer) {
        return (Class)(obj->isa.shiftcls << 3);
    } else {
        return obj->isa.cls;
    }
}

// 设置类指针
void set_class(struct objc_object *obj, Class cls) {
    if (obj->isa.nonpointer) {
        obj->isa.shiftcls = ((uintptr_t)cls) >> 3;
    } else {
        obj->isa.cls = cls;
    }
}

总结

  • 指针类型的 isa 是一个简单的指针,直接指向对象的类。
  • 非指针类型的 isa 是一个包含多个字段的位域,用于存储类指针和附加信息。
  • 优化 isa 提供了更高效的内存使用和性能,通过在 isa 中存储额外的信息来减少对象的大小和提高操作的效率。

通过这种设计,Objective-C 运行时系统可以在保持高效方法调用和内存管理的同时,提供丰富的对象信息和功能。


我调用了一个类方法,但是没有实现,但是根类有个同名的实例方法,他会怎么调用

在 Objective-C 中,当你调用一个类方法(用 + 标记的方法),但是该方法没有在类中实现,而根类(如 NSObject)中有一个同名的实例方法(用 - 标记的方法),系统不会自动调用根类的实例方法。类方法和实例方法是完全独立的,不能相互替代。

具体过程

  1. 方法查找:当你调用一个类方法时,运行时系统会在类的方法列表中查找该方法。
  2. 查找失败:如果在该类中没有找到该类方法,运行时系统会沿着类的继承链向上查找,直到根类(如 NSObject)。
  3. 最终失败:如果在根类中也没有找到该类方法,运行时系统不会去查找同名的实例方法,而是认为该方法不存在。

处理方法未实现的情况

当一个类方法未实现时,运行时系统会转向消息转发机制。下面是消息转发过程的简要介绍:

1. 动态方法解析

首先,运行时系统会调用类方法 +resolveClassMethod:,让你有机会动态添加方法实现。

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(myClassMethod)) {
        // 动态添加方法实现
        class_addMethod(object_getClass(self), sel, (IMP)myClassMethodIMP, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

2. 快速转发

如果动态方法解析没有添加方法实现,运行时系统会调用 -forwardingTargetForSelector: 方法,让你返回一个可以响应该方法的对象。

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(myClassMethod)) {
        return [AnotherClass class];
    }
    return [super forwardingTargetForSelector:aSelector];
}

3. 常规转发

如果快速转发也未处理,运行时系统会调用 -methodSignatureForSelector: 获取方法签名,并调用 -forwardInvocation: 进行消息转发。

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(myClassMethod)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = [anInvocation selector];
    if (sel == @selector(myClassMethod)) {
        // 处理未实现的方法
    } else {
        [super forwardInvocation:anInvocation];
    }
}

总结

  • 类方法和实例方法是独立的:它们不能互相替代,即使名称相同。
  • 消息转发机制:当一个类方法未实现时,运行时系统会通过动态方法解析、快速转发和常规转发来处理未实现的方法,而不会去调用同名的实例方法。

因此,如果你调用一个未实现的类方法,即使根类中有一个同名的实例方法,运行时系统也不会调用该实例方法。你需要通过消息转发机制来处理未实现的类方法。


如果我调用一个类的类方法,但是没有这个方法,有一个同名的实例方法,此时会不会崩溃

如果你调用一个类方法,但是该类没有实现这个方法,同时根类有一个同名的实例方法,程序确实会崩溃。具体来说,当你调用一个类方法时,运行时系统会按照以下步骤查找和处理该方法:

  1. 查找类方法:首先,运行时系统会在类的方法列表中查找该类方法。
  2. 查找父类方法:如果在当前类中没有找到该类方法,系统会沿着继承链向上查找,直到根类(通常是 NSObject)。
  3. 消息转发机制:如果在继承链中也没有找到该类方法,运行时系统会进入消息转发机制。

在消息转发机制中,如果没有适当处理,最终会导致崩溃。具体来说,以下是消息转发的详细步骤:

消息转发机制

1. 动态方法解析

首先,运行时系统会调用 +resolveClassMethod: 方法,尝试动态添加该类方法。如果没有添加该方法,继续下一步。

+ (BOOL)resolveClassMethod:(SEL)sel {
    // 可以在这里动态添加类方法
    return [super resolveClassMethod:sel];
}

2. 快速转发

如果动态方法解析没有添加方法实现,运行时系统会调用 +forwardingTargetForSelector: 方法,尝试找到一个可以响应该消息的对象。

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    // 可以在这里返回一个可以响应该消息的对象
    return [super forwardingTargetForSelector:aSelector];
}

3. 常规转发

如果快速转发也没有处理,运行时系统会调用 +methodSignatureForSelector: 获取方法签名,如果返回 nil,则会直接崩溃。

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 获取方法签名
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    return signature;
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    // 处理未实现的方法
    [super forwardInvocation:anInvocation];
}

具体示例

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
- (void)instanceMethod;
@end

@implementation MyClass
- (void)instanceMethod {
    NSLog(@"Instance method called");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [MyClass performSelector:@selector(instanceMethod)];
    }
    return 0;
}

在上面的示例中,MyClass 类没有实现 +instanceMethod 类方法,而只有 -instanceMethod 实例方法。如果直接调用 [MyClass instanceMethod],运行时系统会按照上述步骤查找类方法,最后会因为找不到类方法并且未正确处理消息转发而崩溃。

崩溃示例

崩溃时会产生类似如下的错误信息:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[MyClass instanceMethod]: unrecognized selector sent to class 0x10050c0a8'

防止崩溃的方法

要防止这种崩溃,你可以通过实现消息转发机制来处理未实现的类方法。例如:

@implementation MyClass

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(instanceMethod)) {
        // 动态添加类方法实现
        class_addMethod(object_getClass(self), sel, (IMP)dynamicClassMethod, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

void dynamicClassMethod(id self, SEL _cmd) {
    NSLog(@"Dynamic class method called");
}

@end

这样,如果调用未实现的 +instanceMethod 类方法,运行时系统会动态添加该方法并避免崩溃。

总结

  • 类方法和实例方法是独立的:调用一个类方法时,系统不会查找同名的实例方法。
  • 未实现的类方法会导致崩溃:如果没有处理消息转发机制,调用未实现的类方法会导致程序崩溃。
  • 消息转发机制可以处理未实现的方法:通过实现 +resolveClassMethod:+forwardingTargetForSelector:+methodSignatureForSelector: 等方法,可以处理未实现的类方法,避免崩溃。

根元类对象的 superclass 指向谁

在 Objective-C 的类层次结构中,根元类(root metaclass)对象的 superclass 指针有一个特定的指向,以保持类结构的完整性和一致性。下面是详细解释:

类和元类的层次结构

  1. 实例对象:每个实例对象的 isa 指针指向其对应的类对象。
  2. 类对象:每个类对象的 isa 指针指向其对应的元类对象。
  3. 元类对象:每个元类对象的 isa 指针指向其超类的元类对象。最终,根元类对象的 isa 指针指向自身。

superclass 指针的指向

  • 普通类的 superclass:普通类的 superclass 指针指向其父类。
  • 元类的 superclass:元类的 superclass 指针指向其父类的元类。

根元类的 superclass 指向

在 Objective-C 中,根类通常是 NSObjectNSProxy。以 NSObject 为例:

  1. NSObject 类对象:其 superclassnil
  2. NSObject 元类对象:其 superclassNSObject 类对象。

这意味着根元类的 superclass 指针指向根类对象本身。

层次结构图示

  • NSObject 类对象
  • isa -> NSObject 元类对象
  • superclass -> nil
  • NSObject 元类对象
  • isa -> NSObject 元类对象(指向自身)
  • superclass -> NSObject 类对象

例子

为了更清晰地理解这个结构,以下是一个示例代码片段:

// NSObject 类对象
Class nsObjectClass = [NSObject class];

// NSObject 元类对象
Class nsObjectMetaClass = object_getClass(nsObjectClass);

// 根类对象的 superclass 为 nil
NSLog(@"NSObject superclass: %@", class_getSuperclass(nsObjectClass)); // 输出: nil

// 元类对象的 superclass 指向根类对象
NSLog(@"NSObject metaclass superclass: %@", class_getSuperclass(nsObjectMetaClass)); // 输出: NSObject

总结

  • 根元类对象的 superclass 指针指向根类对象
  • 根类对象的 superclassnil
  • 元类对象的 superclass 指向其父类的元类对象

这种设计保证了类和元类层次结构的闭环,保持了结构的一致性和完整性。


当调用一个类方法时,如果该类方法没有实现,则会调用同名的实例方法。这样说我有点不理解,应该是只针对NSObject类吧,其他类没有这个特性吧。

你的理解有误。在 Objective-C 中,类方法和实例方法是完全独立的,无论是 NSObject 类还是其他类,它们都没有这个特性,即类方法未实现时不会自动调用同名的实例方法

详细解释

  • 类方法和实例方法的独立性:类方法(标记为 +)和实例方法(标记为 -)是不同的。它们在方法查找、调用和消息转发过程中是分开处理的。调用一个类方法时,系统只会在类方法列表中查找,不会查找实例方法列表。

运行时行为

  1. 方法查找:当你调用一个类方法时,运行时系统首先在类方法列表中查找该方法。
  2. 继承链查找:如果在当前类中没有找到,系统会沿着继承链向上查找,直到根类(通常是 NSObjectNSProxy)。
  3. 消息转发:如果在整个继承链中都没有找到该方法实现,系统会进入消息转发机制。

消息转发机制

在消息转发机制中,如果没有处理,最终会导致崩溃。具体来说,以下是消息转发的详细步骤:

动态方法解析

运行时系统会首先调用 +resolveClassMethod: 方法,让你有机会动态添加该类方法的实现。

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(myClassMethod)) {
        // 动态添加类方法实现
        class_addMethod(object_getClass(self), sel, (IMP)myClassMethodIMP, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

快速转发

如果动态方法解析没有添加方法实现,运行时系统会调用 +forwardingTargetForSelector: 方法,让你返回一个可以响应该消息的对象。

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(myClassMethod)) {
        return [AnotherClass class];
    }
    return [super forwardingTargetForSelector:aSelector];
}

常规转发

如果快速转发也没有处理,运行时系统会调用 +methodSignatureForSelector: 获取方法签名,如果返回 nil,则会直接崩溃。

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(myClassMethod)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = [anInvocation selector];
    if (sel == @selector(myClassMethod)) {
        // 处理未实现的方法
    } else {
        [super forwardInvocation:anInvocation];
    }
}

示例

@interface MyClass : NSObject
- (void)myInstanceMethod;
@end

@implementation MyClass
- (void)myInstanceMethod {
    NSLog(@"Instance method called");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 这是调用类方法
        [MyClass myInstanceMethod];
    }
    return 0;
}

在上面的示例中,MyClass 类没有实现 +myInstanceMethod 类方法,只有 -myInstanceMethod 实例方法。如果直接调用 [MyClass myInstanceMethod],程序会崩溃,因为系统不会自动调用同名的实例方法。

总结

  • 类方法和实例方法是独立的:调用类方法时,系统不会查找同名的实例方法。
  • NSObject 类没有特殊处理NSObject 类以及所有其他类都是如此,类方法和实例方法的查找、调用和消息转发机制是统一的,不存在特殊处理。
  • 消息转发机制:通过实现 +resolveClassMethod:+forwardingTargetForSelector:+methodSignatureForSelector: 等方法,可以处理未实现的类方法,避免崩溃,但不会涉及实例方法的调用。

因此,类方法未实现时不会调用同名的实例方法,不论是 NSObject 类还是其他类,情况都是如此。


import "Mobile.h"

@interface Phone : Mobile @end @implementation Phone - (id)init { self = [super init];

if (self) { NSLog(@"%0", NSStringFromClass([self class])); NSLog(@"%@", NSStringFromClass ([super flass])); } return self; }

你在代码示例中遇到了一些问题,我将帮助你纠正这些问题,并解释一些关键点。

修正后的代码示例

  1. 确保类定义和导入头文件正确。
  2. 修正方法 NSStringFromClass 的调用,确保输出正确。
  3. 修正花括号和其他语法错误。
#import "Mobile.h"

@interface Phone : Mobile
@end

@implementation Phone

- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}

@end

解释

selfsuper

  • self 是指当前实例对象。
  • super 是指向当前实例的父类部分,调用 super 时会告诉编译器去父类的方法中查找实现。

[self class][super class]

  • [self class] 调用会返回当前实例的类名。
  • [super class] 实际上也是返回当前实例的类名,因为 super 并不改变 self 指向的对象,只是改变了方法查找路径。

因此,无论是 [self class] 还是 [super class],都会返回当前实例的类名。在你的代码中,NSLog(@"%@", NSStringFromClass([self class]));NSLog(@"%@", NSStringFromClass([super class])); 都会打印 Phone

代码执行结果

在你的 Phone 类的 init 方法中,如果创建一个 Phone 的实例:

Phone *phone = [[Phone alloc] init];

输出结果将是:

Phone
Phone

这表明无论是 [self class] 还是 [super class],都返回了当前实例的类名 Phone

注意事项

  1. 确保 Mobile 类已经正确定义并包含在项目中。
  2. 注意 Objective-C 的语法和代码格式。

希望这能帮助你理解 selfsuper 的用法以及 [self class][super class] 的区别。如有进一步问题,请随时提问。


没懂,继续深一步给我解释,self class 是不是 等于 [Phone class], 这是一个类对象?这个 class 方法是不是 会到根元类中查找 也就是 nsobject 的 元类。找到了就调用 返回的是啥呢。 super class 是不是相当于 Moble class],也是最终到 nsobject 中的根元类中查找?那返回的是啥呢。

self classsuper class 的解释

self class

[self class] 调用的是当前实例的 class 方法。这意味着它会返回当前对象所属的类。

  1. 实例对象:当你在一个实例方法中调用 [self class] 时,它会返回当前实例对象的类,即实例对象的 isa 指针所指向的类。
  2. 类对象:如果你调用 [Phone class],这将返回 Phone 类对象。

super class

[super class] 也是调用当前实例的 class 方法,但用 super 的语法提示编译器从父类中查找方法实现。不过,class 方法是继承自 NSObject 的,所以无论是通过 self 还是 super 调用,它们最终调用的是 NSObjectclass 方法。

深入理解类对象和元类

  1. 类对象[Phone class] 返回 Phone 类对象。
  2. 元类对象:每个类对象的 isa 指针指向其对应的元类对象。

class 方法的查找过程

  • [self class][super class] 最终都会调用 NSObjectclass 方法。
  • class 方法会返回调用者的类,即当前对象的 isa 指针所指向的类。

代码示例和执行流程

#import "Mobile.h"

@interface Phone : Mobile
@end

@implementation Phone

- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class])); // Line A
        NSLog(@"%@", NSStringFromClass([super class])); // Line B
    }
    return self;
}

@end
  • Line ANSStringFromClass([self class]) 调用时,[self class] 返回当前实例的类,即 Phone
  • Line BNSStringFromClass([super class]) 调用时,虽然语法使用 super,但依旧返回当前实例的类,即 Phone

实际执行过程

  1. 创建实例objective-c Phone *phone = [[Phone alloc] init];

  2. [self class] 调用

    • 调用 Phone 实例的 class 方法。
    • 通过 isa 指针查找到 Phone 类对象。
    • 返回 Phone 类对象。
  3. [super class] 调用

    • 调用 Phone 实例的 class 方法。
    • super 语法会查找父类的 class 方法,但返回值仍然是当前对象的类。
    • 结果与 [self class] 相同,返回 Phone 类对象。

总结

  • [self class] 返回的是当前实例的类,即 Phone
  • [super class] 实际上也是返回当前实例的类,即 Phone,因为 super 只是改变了方法查找路径,并没有改变实际调用者的 isa 指针。

不论使用 [self class] 还是 [super class],最终都返回当前实例的类名,因为 class 方法定义在 NSObject 中,而所有类最终都继承自 NSObjectclass 方法返回的就是调用者的 isa 指针指向的类。

这样一来,无论是通过 self 还是 super 调用 class 方法,都返回当前对象所属的类。


动态添加方法

在 Objective-C 中,动态添加方法是一种强大的功能,允许你在运行时向类中添加方法。这通常在以下几种情况下非常有用:

  1. 动态扩展类的功能。
  2. 在运行时处理未知选择器。
  3. 实现消息转发机制。

动态添加方法的步骤

  1. 定义方法的实现:实现一个 C 函数,作为动态添加方法的实现。
  2. 动态添加方法:使用 class_addMethod 函数将该实现添加到类中。
  3. 处理未实现的方法:通过 resolveInstanceMethodresolveClassMethod 方法来处理未实现的方法。

代码示例

以下是一个完整的示例,展示了如何动态添加一个实例方法和类方法。

定义类和方法实现

首先,定义一个类 MyClass 和一个动态添加的方法实现。

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
@end

// 动态添加的方法实现
void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method -dynamicMethod called");
}

动态添加实例方法

在类的实现中,重写 +resolveInstanceMethod: 方法,以动态添加方法。

@implementation MyClass

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end

动态添加类方法

类似地,重写 +resolveClassMethod: 方法,以动态添加类方法。

@implementation MyClass (DynamicClassMethod)

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(dynamicClassMethod)) {
        Class metaClass = objc_getMetaClass(class_getName([self class]));
        class_addMethod(metaClass, sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

@end

使用动态添加的方法

最后,在主函数中调用这些方法。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myObject = [[MyClass alloc] init];

        // 调用动态添加的实例方法
        if ([myObject respondsToSelector:@selector(dynamicMethod)]) {
            [myObject performSelector:@selector(dynamicMethod)];
        }

        // 调用动态添加的类方法
        if ([MyClass respondsToSelector:@selector(dynamicClassMethod)]) {
            [MyClass performSelector:@selector(dynamicClassMethod)];
        }
    }
    return 0;
}

代码解释

  1. 动态添加实例方法

    • +resolveInstanceMethod: 方法在实例方法查找失败时被调用。
    • 如果方法选择器是 @selector(dynamicMethod),则使用 class_addMethod 动态添加该方法。
    • class_addMethod 的参数包括类、选择器、方法实现和类型编码。
    • IMP 类型是指向方法实现的指针。
    • "v@:" 是类型编码,表示返回类型为 void,参数为 self_cmd
  2. 动态添加类方法

    • +resolveClassMethod: 方法在类方法查找失败时被调用。
    • 使用 objc_getMetaClass 获取元类,然后使用 class_addMethod 向元类添加方法。
    • 元类的类型编码与实例方法相同。

运行结果

当运行上述代码时,会看到如下输出:

Dynamic method -dynamicMethod called
Dynamic method -dynamicMethod called

这表明动态添加的实例方法和类方法都被成功调用。

总结

  • 动态方法解析:通过重写 +resolveInstanceMethod:+resolveClassMethod: 方法,可以在运行时动态添加方法。
  • 方法添加:使用 class_addMethod 将方法添加到类或元类中。
  • 方法实现:动态添加的方法实现可以是一个 C 函数,指向 IMP 类型。

这种技术在许多高级场景中非常有用,例如为现有类添加功能、处理未知消息、实现消息转发等。


我有个问题,就是我调用的这个方法,我怎么先判断一下 这个方法是否存在,如果不存在 我在动态添加啊

在 Objective-C 中,可以使用 respondsToSelector: 方法来检查某个对象是否能够响应特定的选择器(即方法)。对于类方法,可以使用 instancesRespondToSelector: 方法来检查类是否能够响应特定的实例方法选择器。通过这些方法,可以在调用方法之前检查其存在性,如果不存在,则动态添加该方法。

判断方法是否存在并动态添加方法

以下是一个示例,展示如何在调用方法之前检查其存在性,如果方法不存在则动态添加该方法。

定义类和方法实现

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
@end

// 动态添加的方法实现
void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method -dynamicMethod called");
}

动态添加实例方法的代码

在类的实现中,重写 +resolveInstanceMethod: 方法,并添加一个检查方法存在性的函数。

@implementation MyClass

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

// 检查并动态添加方法
+ (void)checkAndAddMethod:(SEL)sel {
    if (![self instancesRespondToSelector:sel]) {
        [self resolveInstanceMethod:sel];
    }
}

@end

动态添加类方法的代码

类似地,为类方法添加检查和动态添加代码。

@implementation MyClass (DynamicClassMethod)

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(dynamicClassMethod)) {
        Class metaClass = objc_getMetaClass(class_getName([self class]));
        class_addMethod(metaClass, sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

// 检查并动态添加类方法
+ (void)checkAndAddClassMethod:(SEL)sel {
    if (![self respondsToSelector:sel]) {
        [self resolveClassMethod:sel];
    }
}

@end

使用动态添加的方法

在主函数中,先检查方法是否存在,如果不存在则动态添加方法,然后调用该方法。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myObject = [[MyClass alloc] init];

        // 检查并添加实例方法
        [MyClass checkAndAddMethod:@selector(dynamicMethod)];
        if ([myObject respondsToSelector:@selector(dynamicMethod)]) {
            [myObject performSelector:@selector(dynamicMethod)];
        }

        // 检查并添加类方法
        [MyClass checkAndAddClassMethod:@selector(dynamicClassMethod)];
        if ([MyClass respondsToSelector:@selector(dynamicClassMethod)]) {
            [MyClass performSelector:@selector(dynamicClassMethod)];
        }
    }
    return 0;
}

解释

  1. 判断实例方法是否存在
  2. + (void)checkAndAddMethod:(SEL)sel 方法使用 instancesRespondToSelector: 来检查实例方法是否存在。
  3. 如果不存在,则调用 resolveInstanceMethod: 动态添加方法。

  4. 判断类方法是否存在

  5. + (void)checkAndAddClassMethod:(SEL)sel 方法使用 respondsToSelector: 来检查类方法是否存在。
  6. 如果不存在,则调用 resolveClassMethod: 动态添加方法。

  7. 调用方法

  8. 在检查并动态添加方法后,使用 performSelector: 来调用方法。

运行结果

当运行上述代码时,如果 dynamicMethoddynamicClassMethod 方法不存在,它们将被动态添加,然后成功调用:

Dynamic method -dynamicMethod called
Dynamic method -dynamicMethod called

总结

通过使用 respondsToSelector:instancesRespondToSelector: 方法,可以在运行时检查方法的存在性,并在必要时动态添加方法。这种机制可以在调用方法之前确保方法的存在,从而提高代码的灵活性和健壮性。


注册类

在 Objective-C 中,动态注册类是一种高级功能,可以在运行时创建新的类。这种功能主要通过运行时库函数来实现,特别是 objc_allocateClassPairobjc_registerClassPair 函数。

注册类的步骤

  1. 分配类对(Allocate Class Pair):使用 objc_allocateClassPair 创建一个新的类和它的元类。
  2. 添加成员变量和方法:向新类中添加实例变量和方法。
  3. 注册类对(Register Class Pair):使用 objc_registerClassPair 注册类和元类,使其可以在程序中使用。

代码示例

以下是一个完整的示例,展示了如何动态注册一个新的类,并向其添加方法和实例变量。

导入运行时库

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

动态方法实现

定义一个动态添加的方法实现:

void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method -dynamicMethod called");
}

注册类

实现动态注册类的代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 动态创建一个新类 MyDynamicClass,它继承自 NSObject
        Class MyDynamicClass = objc_allocateClassPair([NSObject class], "MyDynamicClass", 0);

        // 添加实例变量
        class_addIvar(MyDynamicClass, "dynamicVariable", sizeof(NSString *), log2(sizeof(NSString *)), "@");

        // 添加实例方法
        BOOL methodAdded = class_addMethod(MyDynamicClass, @selector(dynamicMethod), (IMP)dynamicMethodIMP, "v@:");
        if (methodAdded) {
            NSLog(@"Method -dynamicMethod added successfully");
        } else {
            NSLog(@"Failed to add method -dynamicMethod");
        }

        // 注册类
        objc_registerClassPair(MyDynamicClass);

        // 创建动态类的实例
        id dynamicInstance = [[MyDynamicClass alloc] init];

        // 设置实例变量
        NSString *dynamicValue = @"Hello, Dynamic World!";
        [dynamicInstance setValue:dynamicValue forKey:@"dynamicVariable"];

        // 获取实例变量
        NSString *retrievedValue = [dynamicInstance valueForKey:@"dynamicVariable"];
        NSLog(@"Retrieved value from dynamicVariable: %@", retrievedValue);

        // 调用动态添加的方法
        if ([dynamicInstance respondsToSelector:@selector(dynamicMethod)]) {
            [dynamicInstance performSelector:@selector(dynamicMethod)];
        }
    }
    return 0;
}

代码解释

  1. 分配类对
  2. objc_allocateClassPair 函数用于创建一个新的类和它的元类。这个函数需要三个参数:父类、类名和额外的内存大小(通常为0)。
  3. 在这个示例中,MyDynamicClass 继承自 NSObject

  4. 添加成员变量

  5. class_addIvar 函数用于向类添加实例变量。
  6. 参数包括类、变量名、变量大小、对齐方式和编码类型。

  7. 添加实例方法

  8. class_addMethod 函数用于向类添加实例方法。
  9. 参数包括类、选择器、方法实现(IMP)和类型编码。
  10. 在这个示例中,添加了一个名为 dynamicMethod 的方法。

  11. 注册类对

  12. objc_registerClassPair 函数用于注册类和元类,使其可以在程序中使用。

  13. 使用动态类

  14. 创建动态类的实例:[[MyDynamicClass alloc] init]
  15. 设置和获取实例变量:使用 setValue:forKey:valueForKey:
  16. 调用动态添加的方法:使用 performSelector:

运行结果

当运行上述代码时,会看到如下输出:

Method -dynamicMethod added successfully
Retrieved value from dynamicVariable: Hello, Dynamic World!
Dynamic method -dynamicMethod called

这表明动态类的实例变量和方法都被成功设置和调用。

总结

  • 动态注册类:通过 objc_allocateClassPairobjc_registerClassPair 函数,可以在运行时创建和注册新的类。
  • 添加成员变量和方法:使用 class_addIvarclass_addMethod 函数,可以向新类添加实例变量和方法。
  • 灵活性和动态性:这种机制提供了极大的灵活性,允许你在运行时扩展类的功能,实现动态行为。

runtime 动态添加方法 和 performselector 啥关系

在 Objective-C 中,runtime 动态添加方法与 performSelector 方法有密切关系。动态添加方法允许你在运行时向类或实例添加方法,而 performSelector 方法则用于在运行时调用一个选择器对应的方法。理解它们的关系和交互可以帮助你更好地利用 Objective-C 的动态特性。

动态添加方法

通过 runtime 机制,你可以在运行时向类或实例动态添加方法。这在某些场景下非常有用,例如扩展现有类的功能,或者处理未知选择器。

示例:动态添加方法

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

// 动态添加的方法实现
void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method -dynamicMethod called");
}

@interface MyClass : NSObject
@end

@implementation MyClass
@end

动态添加方法的代码

在类的实现中,通过重写 +resolveInstanceMethod: 方法来动态添加方法。

@implementation MyClass

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end

使用 performSelector 调用方法

performSelector 方法允许你在运行时调用一个选择器对应的方法。这对于调用在编译时并不确定的方法非常有用,尤其是在你动态添加方法的场景中。

示例:使用 performSelector 调用动态添加的方法

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myObject = [[MyClass alloc] init];

        // 尝试调用 dynamicMethod 方法
        if ([myObject respondsToSelector:@selector(dynamicMethod)]) {
            [myObject performSelector:@selector(dynamicMethod)];
        }
    }
    return 0;
}

解释 performSelector 和动态添加方法的关系

  1. 方法查找和动态添加

    • 当你调用 performSelector: 时,系统会首先在当前对象的方法列表中查找对应的选择器。
    • 如果没有找到对应的方法实现,系统会调用 +resolveInstanceMethod: 方法(对于类方法,调用 +resolveClassMethod:)。
    • +resolveInstanceMethod: 方法中,你可以动态添加该选择器对应的方法实现。
  2. 动态添加后的调用

    • 一旦方法被动态添加,再次调用 performSelector: 时,就能够找到并调用刚刚添加的方法。

总结

  • 动态添加方法:通过 runtime 机制,可以在运行时向类或实例动态添加方法。这通常在 +resolveInstanceMethod:+resolveClassMethod: 方法中实现。
  • performSelector 调用performSelector: 方法用于在运行时调用一个选择器对应的方法。它依赖于方法查找机制,因此如果在首次调用时没有找到方法,可以通过 resolveInstanceMethod: 动态添加该方法。
  • 关系:在首次调用 performSelector: 时,如果方法不存在,可以通过 resolveInstanceMethod: 动态添加该方法,之后再次调用 performSelector: 时,方法已经存在,可以直接调用。

通过这种机制,Objective-C 提供了高度的灵活性和动态性,使得程序可以在运行时根据需要添加或调用方法,从而实现更复杂和动态的行为。


动态添加的方法 能通过 【obj xxx] 这样调用么,还是只能使用 performselector 调用

在 Objective-C 中,动态添加的方法可以通过 [obj methodName] 这种常规方法调用方式使用,也可以通过 performSelector 调用。两者没有区别,都是通过 Objective-C 的方法调用机制来执行的。

通过常规方式调用动态添加的方法

以下是一个完整的示例,展示如何动态添加方法并通过常规方法调用它。

定义类和方法实现

首先,定义一个类 MyClass 和一个动态添加的方法实现:

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
@end

// 动态添加的方法实现
void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method -dynamicMethod called");
}

动态添加实例方法

在类的实现中,重写 +resolveInstanceMethod: 方法,以动态添加方法:

@implementation MyClass

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end

使用动态添加的方法

在主函数中,通过常规方法调用和 performSelector 调用动态添加的方法:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myObject = [[MyClass alloc] init];

        // 使用常规方式调用动态添加的方法
        if ([myObject respondsToSelector:@selector(dynamicMethod)]) {
            [myObject dynamicMethod];
        }

        // 使用 performSelector 调用动态添加的方法
        if ([myObject respondsToSelector:@selector(dynamicMethod)]) {
            [myObject performSelector:@selector(dynamicMethod)];
        }
    }
    return 0;
}

代码解释

  1. 动态添加实例方法

    • +resolveInstanceMethod: 方法中,检查选择器是否为 @selector(dynamicMethod)
    • 如果是,则使用 class_addMethod 函数将 dynamicMethodIMP 方法实现添加到 MyClass 类中。
    • 返回 YES 表示方法添加成功。
  2. 使用常规方式调用方法

    • 检查对象是否响应 dynamicMethod 方法(即该方法是否存在)。
    • 如果存在,直接通过 [myObject dynamicMethod] 调用方法。
  3. 使用 performSelector 调用方法

    • 同样的检查对象是否响应 dynamicMethod 方法。
    • 如果存在,通过 [myObject performSelector:@selector(dynamicMethod)] 调用方法。

总结

  • 常规方法调用:可以直接通过 [obj methodName] 调用动态添加的方法,只要在调用之前确保该方法已经添加。
  • performSelector 调用:可以通过 performSelector 调用动态添加的方法,同样需要确保方法已经添加。
  • 方法查找机制:无论是通过常规方式还是 performSelector 调用,Objective-C 的方法查找机制都会确保在调用方法之前执行 +resolveInstanceMethod:+resolveClassMethod: 来动态添加方法。

通过这种机制,Objective-C 提供了灵活的动态方法处理能力,使得程序可以在运行时根据需要添加或调用方法,从而实现更复杂和动态的行为。