当前位置: 首页 > news >正文

深度对比:Objective-C与Swift的RunTime机制与底层原理

1. RunTime简介

RunTime(运行时)是指程序在运行过程中动态管理类型、对象、方法等的机制。Objective-C 和 Swift 都拥有自己的运行时系统,但设计理念和实现方式有很大不同。理解 RunTime 的底层原理,是掌握 iOS 高级开发的关键。

2. Objective-C RunTime底层原理

2.1 对象结构与isa指针

2.1.1 OC对象的本质

Objective-C 对象在底层以结构体的形式实现,其最核心的成员是 isa 指针。

struct objc_object {Class isa;
};
  • objc_object 是所有 OC 对象的基类结构体,只包含 isa 指针。
  • 每个实例对象的内存布局:isa 指针 + 父类成员变量 + 本类成员变量(由编译器自动生成)。
  • Class 实际上是 objc_class 类型,指向类对象。
  • 每个对象通过 isa 指针找到自己的类对象,类对象再通过 isa 指针找到元类对象。

2.1.2 isa指针的优化(isa_t)

isa 指针在 64 位系统下被设计为共用体(union),不仅存储类对象地址,还包含如是否为 Tagged Pointer、部分标志位等信息。引用计数等信息通常存储在 side table 中,只有部分位(如 extra_rc)用于优化。

union isa_t {isa_t() { }isa_t(uintptr_t value) : bits(value) { }Class cls;uintptr_t bits;struct {uintptr_t nonpointer        : 1;uintptr_t has_assoc         : 1;uintptr_t has_cxx_dtor      : 1;uintptr_t shiftcls          : 33;uintptr_t magic             : 6;uintptr_t weakly_referenced : 1;uintptr_t deallocating      : 1;uintptr_t has_sidetable_rc  : 1;uintptr_t extra_rc          : 19;};
};
  • nonpointer:是否为优化过的isa指针
  • shiftcls:类对象地址
  • extra_rc:引用计数
  • has_assoc:是否有关联对象

Tagged Pointer:对于部分小对象(如小范围的 NSNumber、NSDate),系统会直接将值编码在指针中,从而提升性能。

2.1.3 OC对象的内存布局

实际内存布局顺序为:

  1. isa指针(对象头部,8字节/4字节,取决于架构)
  2. 父类的成员变量(从NSObject/父类开始,依次向下排列)
  3. 本类声明的成员变量(按声明顺序排列)

以下代码为例:

@interface Animal : NSObject {int age;int height;
}
@end@interface Dog : Animal {int weight;int color;
}
@end

Dog对象的内存布局:

| isa | age | height | weight | color |

|-----|-------|----------|-----------|--------|

  • isa:指向类对象
  • age、height:Animal父类成员变量
  • weight、color:Dog本类成员变量

这样设计可以保证子类对象在作为父类对象使用时,父类成员变量的偏移量不变,从而保证继承的兼容性和对象模型的稳定性。

2.2 类对象与元类对象

2.2.1 类对象(Class)

类对象本质也是一个结构体,定义如下(简化版):

struct objc_class : objc_object {Class isa;Class super_class;cache_t cache;class_data_bits_t bits;// 下面是bits指向的数据结构(class_rw_t/class_ro_t),用于描述类的详细信息// 这里只做伪代码展示struct class_rw_t {uint32_t flags;                // 标志位uint32_t version;              // 版本号const class_ro_t *ro;          // 指向只读数据method_list_t *methods;        // 方法列表property_list_t *properties;   // 属性列表protocol_list_t *protocols;    // 协议列表ivar_list_t *ivars;            // 成员变量列表// ... 其他运行时扩展信息};struct class_ro_t {uint32_t flags;                // 标志位uint32_t instanceStart;        // 实例对象起始偏移uint32_t instanceSize;         // 实例对象大小const char *name;              // 类名method_list_t *methods;        // 方法列表property_list_t *properties;   // 属性列表protocol_list_t *protocols;    // 协议列表ivar_list_t *ivars;            // 成员变量列表// ... 其他只读信息};
};
  • isa:指向元类对象
  • super_class:指向父类对象
  • cache:方法缓存
  • class_data_bits_t:存储方法列表、属性列表、协议列表等

2.2.2 元类对象(Meta-Class)

  • 元类对象用于存储类方法的实现,本质上类方法就是元类的实例方法。
  • 元类的isa指向根元类(NSObject的Meta-Class)
  • 元类的super_class指向父类的元类
  • 根元类的isa指针指向自己

2.2.3 内存结构图

  • 类对象和元类对象本质上都是objc_class结构体
  • 类对象存储实例方法,元类对象存储类方法

2.3 方法缓存与方法列表

2.3.1 方法缓存(cache_t)

每个类对象都有一个方法缓存cache,结构如下:

struct cache_t {struct bucket_t *_buckets;mask_t _mask;mask_t _occupied;
};
  • _buckets:哈希表,存储 SEL(方法选择子)和 IMP(实现指针)的映射关系。
  • _mask、_occupied:用于哈希冲突处理

作用:加速方法查找,避免每次都遍历方法列表。

2.3.2 方法列表

类对象中有方法列表,存储所有实例方法:

struct method_t {SEL name;      // 方法名const char *types; // 方法类型编码IMP imp;       // 方法实现指针
};
  • 方法查找顺序:先查缓存,再查方法列表,最后查父类

2.4 成员变量与属性

  • 成员变量(Ivar)

在类对象的 ivar_list 中,保存的是所有成员变量的描述信息(如名字、类型、偏移量等),而成员变量的实际值是存储在每个实例对象的内存空间中(紧跟在 isa 指针之后,按照继承链和声明顺序排列)。

    • 属性(Property)

    属性信息存储在类对象的 property_list 中,仅用于描述属性的名称、类型、特性等。属性通常会自动合成一个对应的成员变量(如 name),成员变量的实际值存储在实例对象中。

    // 成员变量描述结构体
    struct ivar_t {int32_t *offset;      // 偏移量(成员变量在实例对象内存中的位置)const char *name;     // 成员变量名const char *type;     // 类型编码// ... 其他信息
    };// 成员变量列表
    struct ivar_list_t {uint32_t count;       // 成员变量数量ivar_t ivars[];       // 成员变量数组
    };// 属性描述结构体
    struct property_t {const char *name;     // 属性名const char *attributes; // 属性特性字符串(如T@"NSString",C,N,V_name等)// ... 其他信息
    };// 属性列表
    struct property_list_t {uint32_t count;         // 属性数量property_t properties[];// 属性数组
    };
    

    3. Objective-C消息机制与方法查找——RunTime的灵魂

    3.1 objc_msgSend流程

    Objective-C 的方法调用本质上是“向对象发送消息”,而 objc_msgSend 就是消息发送的核心函数。理解其底层流程,是掌握 RunTime 的关键。

    1. 空对象判断(Nil Check)

    • 首先判断消息接收者(对象指针)是否为 nil。
    • 如果是 nil,objc_msgSend 直接返回 0,不做任何操作(这也是 OC 调用 nil 对象不会崩溃的原因)。

    2. 获取类对象(Class Lookup)

    • 通过对象头部的 isa 指针,获取当前对象的类对象(Class)。

    3. 方法缓存查找(Cache Lookup)

    • 优先在类对象的缓存(cache_t)中查找方法实现(IMP)。
    • 如果缓存命中,直接跳转到对应的 IMP 执行,速度极快。

    4. 方法列表查找(Method List Lookup)

    • 如果缓存未命中,则遍历类对象的方法列表(method_list_t),查找与 SEL(方法选择子)匹配的方法。
    • 找到后,将 SEL 和 IMP 加入缓存,便于下次快速查找。

    5. 父类链查找(Superclass Lookup)

    • 如果当前类的方法列表中仍未找到目标方法,则顺着 super_class 指针,递归查找父类的方法缓存和方法列表,直到 NSObject 为止。

    6. 动态方法解析(Dynamic Method Resolution)

    • 如果在整个继承链上都找不到目标方法,RunTime 会尝试动态方法解析。
    • 通过调用 +resolveInstanceMethod: 或 +resolveClassMethod:,允许开发者动态添加方法实现。
    • 如果在回调中通过 class_addMethod 成功添加了方法,RunTime 会重新走一次方法查找流程。

    7. 消息转发机制(Message Forwarding)

    • 如果动态方法解析仍未能处理该消息,进入消息转发阶段:
    • 首先调用 -forwardingTargetForSelector:,允许指定其他对象响应该消息。
    • 如果还未处理,则调用 -methodSignatureForSelector: 和 -forwardInvocation:,开发者可自定义消息处理逻辑。

    8. 抛出异常(Unrecognized Selector)

    • 如果所有机制都未能处理该消息,RunTime 最终会抛出 unrecognized selector sent to instance 异常,导致程序崩溃。

    3.2 消息转发三步曲

    当对象接收到一个未实现的方法调用时,RunTime 会依次经历以下步骤:

    1. 动态方法解析(resolveInstanceMethod:、resolveClassMethod:)
    2. 快速消息转发(forwardingTargetForSelector:)
    3. 完整消息转发(methodSignatureForSelector: & forwardInvocation:)

    下面我们逐步拆解每一步的底层实现。

    3.2.1 动态方法解析(resolveInstanceMethod、resolveClassMethod)

    底层原理:

    当实例方法找不到时,RunTime 会调用 +resolveInstanceMethod:;类方法找不到时,调用 +resolveClassMethod:。你可以在这里通过 class_addMethod 动态添加方法。

    代码示例:

    // 动态添加实例方法
    + (BOOL)resolveInstanceMethod:(SEL)sel {if (sel == @selector(test)) {class_addMethod(self, sel, (IMP)testIMP, "v@:");return YES;}return [super resolveInstanceMethod:sel];
    }// 动态添加类方法
    + (BOOL)resolveClassMethod:(SEL)sel {if (sel == @selector(testClassMethod)) {Class metaClass = object_getClass(self); // 获取元类class_addMethod(metaClass, sel, (IMP)testClassMethodIMP, "v@:");return YES;}return [super resolveClassMethod:sel];
    }
    • 如果返回 YES,RunTime 会重新查找方法并执行。
    • 如果返回 NO 或未处理,进入下一步。

    3.2.2 快速消息转发(forwardingTargetForSelector:)

    底层原理:

    如果动态方法解析未能处理,RunTime 会调用实例方法 -forwardingTargetForSelector:。你可以返回一个“备选接收者”,RunTime 会将消息直接转发给该对象。如果返回 nil,进入完整消息转发。

    代码示例:

    - (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(test)) {return anotherObj; // 转发给 anotherObj}return [super forwardingTargetForSelector:aSelector];
    }

      3.2.3 获取方法签名(methodSignatureForSelector:)

      底层原理:

      如果快速转发未处理,RunTime 会调用 -methodSignatureForSelector:,询问你“这个 SEL 对应的方法签名是什么?”你需要返回一个 NSMethodSignature 对象,描述方法的参数和返回值类型。如果返回 nil,RunTime 会直接抛出异常。

      代码示例:

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

      3.2.4 自定义消息处理(forwardInvocation:)

      底层原理:

      RunTime 会根据方法签名创建一个 NSInvocation 对象,封装原始的消息调用。然后调用 -forwardInvocation:,你可以在这里自定义如何处理这个消息,比如转发给其他对象、修改参数、记录日志等。如果你没有处理,调用 [super forwardInvocation:],最终会抛出异常。

      代码示例:

      - (void)forwardInvocation:(NSInvocation *)anInvocation {if ([anotherObj respondsToSelector:anInvocation.selector]) {[anInvocation invokeWithTarget:anotherObj];} else {[super forwardInvocation:anInvocation];}
      }

      3.3 抛出异常(unrecognized selector)

      1. 底层原理

      如果上述所有机制都未能处理该消息,RunTime 会调用 doesNotRecognizeSelector:,最终抛出 unrecognized selector sent to instance 异常,导致程序崩溃。

      2. 异常信息解读

      当出现此异常时,Xcode控制台会输出类似如下信息:

      Terminating app due to uncaught exception 'NSInvalidArgumentException', 
      reason: '-[MyClass test]: unrecognized selector sent to instance 0x12345678'
      • MyClass:出错的类名
      • test:未识别的方法名
      • 0x12345678:对象内存地址

      4. Swift RunTime机制深度剖析

      4.1 Swift对象模型:值类型与引用类型的本质

      4.1.1 值类型与引用类型的区别

      在Swift中,对象主要分为两种类型:值类型和引用类型。这两种类型在内存管理和使用方式上有着本质的区别:

      • 值类型:包括struct和enum,数据直接存储在变量本身,通常在栈上分配,内存布局紧凑,访问效率高。赋值时进行值拷贝。
      • 引用类型:主要指class,数据存储在堆上,变量保存的是指向对象的指针。支持继承和多态,赋值时传递引用。

      4.1.2 Swift类对象的内存结构

      Swift的类对象采用了现代化的内存布局设计,其结构如下:

      | Metadata指针 | 属性1 | 属性2 | ... |
      • Metadata指针:指向类型的元数据,类似于Objective-C的isa指针,但包含更丰富的信息
      • 属性区:按顺序存储对象的所有属性值

      4.1.3 继承NSObject的Swift类的内存结构

      继承NSObject的Swift类同时具有Swift和Objective-C的特征

      以下代码为例:

      class MyClass: NSObject {var property1: Intvar property2: String
      }

        其内存布局如下所示:

        +------------------+------------------+------------------+------------------+
        |   isa指针(OC)    | Metadata指针(Swift) |    property1    |    property2    |
        +------------------+------------------+------------------+------------------+
        • 对象头部包含两个指针: Objective-C的isa指针和Swift的Metadata指针。
        • 属性按照Swift的内存布局规则排列。
        • 方法根据是否@objc决定存储位置,objc方法:存储在Objective-C的class_rw_t中,非@objc方法:存储在Swift的虚表中。

        这种混合结构让继承NSObject的Swift类既能保持Swift的高效,又能与Objective-C无缝交互。

        4.1.4 Metadata元数据详解

        Metadata是Swift类型系统的核心,它记录了类型在运行时的所有关键信息。以类(Class)为例,其简化结构如下:

        以类(Class)为例,简化结构如下:

        struct ClassMetadata {void *kind;           // 类型标识(区分class/struct/enum等)void *superclass;     // 父类的Metadata指针void *cacheData;      // 方法缓存(加速查找)void *data;           // 指向更详细的类型描述// ... 其他字段
        };
        Metadata采用分层设计:基础信息在前,详细信息通过指针层层递进。

        1. 基础层:包含类型标识、父类信息等基本信息

        2. 扩展层:过data指针指向更详细的信息,包括:

        • vtable:虚函数表,存储可重写方法的实现指针
        • witness table:协议表,存储协议方法的实现
        • 属性描述:属性名、类型、偏移量等
        • 泛型信息:泛型参数的类型描述

        4.2 Swift方法调用机制:静态与动态的平衡

        4.2.1 静态派发:Swift的默认选择

        Swift默认采用静态派发机制,这意味着:

        • 方法调用在编译期就确定了调用地址。调用时无需查找,直接跳转
        • 适用于struct、final class、private方法等场景
        • 执行效率高,接近C语言的函数调用

        4.2.2 动态派发:特定场景的选择

        在以下情况下,Swift会采用动态派发机制:

        • 被dynamic修饰的方法
        • 遵循@objc协议的方法
        • 继承自NSObject的类中的方法
        • 协议的可选方法

        动态派发通过vtable(虚函数表)或witness table(协议表)实现:

        +-------------------+
        | 方法1的指针(IMP) |
        | 方法2的指针(IMP) |
        | ...               |
        +-------------------+

        4.2.3 方法查找流程对比

        • 静态派发:编译期确定方法地址,直接调用方法实现,无需运行时查找。
        • 动态派发:运行时查找vtable/witness table,获取方法实现指针,调用方法实现。

        4.2.4 与Objective-C的对比

        • OC:所有方法调用都走objc_msgSend,完全动态派发,灵活性高,但性能开销大。
        • Swift:优先使用静态派发,仅在必要时使用动态派发,性能更优,但动态性受限。

        4.3 Swift的反射与元编程能力

        4.3.1 Mirror反射API

        Swift提供了Mirror类型用于运行时反射,可以获取对象的属性名、属性值、类型等信息。

        let mirror = Mirror(reflecting: someObject)
        for child in mirror.children {print("属性名: \(child.label), 属性值: \(child.value)")
        }
        • 只能读取对象信息,不能修改。
        • 支持获取属性名、属性值、类型等信息,使用简单,但功能有限。

        4.3.2 Swift RunTime的局限性

        • 无法动态添加/替换方法:没有公开的RunTime C API
        • 类型信息有限:大部分类型信息在编译期丢失
        • 元编程能力弱:不支持元类、动态创建类等高级特性

        4.3.3 Swift RunTime的设计哲学与优化

        • 类型安全:所有类型信息尽量在编译期确定,避免运行时错误
        • 极致性能:静态派发、紧凑内存布局、去虚表等优化
        • 安全性高:避免了OC RunTime带来的诸多安全隐患

        总结一句话:

        Swift 的 RunTime 机制以牺牲部分动态性为代价,换来了更高的类型安全和运行效率。

        5. Objective-C与Swift RunTime机制对比

        5.1 核心机制对比

        Objective-C RunTimeSwift RunTime
        对象头部isa指针,部分信息存side tableMetadata指针,引用计数直接存对象头部
        方法派发动态派发(objc_msgSend),全部运行时查找静态派发为主,动态派发仅限特殊场景
        动态性极强,支持动态添加/替换方法、类、属性等较弱,类型和方法多在编译期确定
        反射能力强大,支持完整反射和元编程Mirror API有限,无法动态修改类型结构
        内存布局对象头部+成员变量,side table存扩展信息Metadata指针+属性区,布局紧凑
        弱引用全局weak表,side table辅助全局weak表,直接与对象头部配合
        关联对象支持(依赖side table)不支持(标准库无此机制)
        性能动态性带来一定性能损耗静态派发、内存优化,性能极高
        安全性动态性强但易出错类型安全,编译期检查,运行时更安全
        混编支持与C/Swift无缝混编与OC混编需继承NSObject,部分特性受限

        5.2 方法派发机制

        • Objective-C 所有方法调用都通过objc_msgSend来实现,也就是每次调用方法时,系统都会在运行时查找方法的具体实现。这种方式非常灵活,可以在运行时动态替换方法,但也带来了一定的性能损耗。

        • Swift采用以静态派发为主的混合派发机制。大多数方法在编译期就确定了实现位置,只有少数特殊情况(如被dynamic修饰的方法、遵循@objc协议的方法、协议的可选方法、继承自NSObject的类的方法)才会使用动态派发。动态派发通过vtable或witness table实现,虽然限制了动态性,但大大提升了性能。

        5.3 动态性与反射

        • Objective-C具备极强的动态性。开发者可以在运行时动态添加或替换方法、创建新类、修改类结构,还可以通过完整的反射API查询和操作类型信息。这些特性使其非常适合实现AOP、热修复等高级功能。例如,可以通过class_addMethod动态添加方法,通过method_exchangeImplementations交换方法实现。

        • Swift的动态性相对较弱。大多数类型信息在编译期就已确定,运行时修改能力有限。虽然提供了Mirror API用于查看对象属性和类型信息,但无法动态修改类型结构。这种设计虽然限制了某些高级特性的实现,但更适合类型安全的业务开发。例如,可以通过Mirror(reflecting: object)查看对象属性。

        5.4 内存布局与side table

        • Objective-C的对象内存布局包含isa指针(8字节)和按声明顺序排列的成员变量。扩展信息(如引用计数、弱引用、关联对象、方法缓存等)存储在side table中。这种设计虽然减少了对象本身的内存占用,但访问这些信息需要额外查找side table,增加了访问成本。

        • Swift的对象内存布局包含Metadata指针和按声明顺序排列的属性。引用计数直接存储在对象头部,弱引用与对象头部配合,没有公开的side table机制。这种设计虽然增加了对象本身的大小,但减少了访问开销,整体内存布局更紧凑高效。

        5.5 实际开发影响

        • 在框架开发中,Objective-C更适合需要高度动态性的场景,如AOP实现、热修复系统、插件化架构、动态代理、KVO/KVC等。可以通过减少消息发送、缓存方法实现、优化内存布局、合理使用side table等方式优化性能。

        • Swift更适合高性能业务逻辑、类型安全API、并发编程、函数式编程等场景。可以通过利用静态派发、优化值类型使用、减少动态派发、优化内存布局等方式提升性能。在混编开发中,需要明确边界、合理分工、注意性能、统一规范,根据具体需求选择合适的语言或混编方案。

        6. RunTime的实际应用场景与代码示例

        6.1 Objective-C RunTime常见用法

        6.1.1 方法交换(Method Swizzling)

        场景:无侵入埋点、AOP、日志等。

        示例:统计所有UIViewController的viewDidLoad调用。

        #import <objc/runtime.h>@implementation UIViewController (Swizzling)+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{Method original = class_getInstanceMethod(self, @selector(viewDidLoad));Method swizzled = class_getInstanceMethod(self, @selector(swizzled_viewDidLoad));method_exchangeImplementations(original, swizzled);});
        }- (void)swizzled_viewDidLoad {NSLog(@"%@ did load", NSStringFromClass([self class]));[self swizzled_viewDidLoad]; // 实际调用原viewDidLoad
        }@end

        6.1.2 动态添加属性(关联对象)

        场景:为Category动态添加属性。

        #import <objc/runtime.h>static const void *kNameKey = &kNameKey;@interface Person : NSObject
        @end@implementation Person
        @end@interface Person (Category)
        @property (nonatomic, strong) NSString *name;
        @end@implementation Person (Category)- (void)setName:(NSString *)name {objc_setAssociatedObject(self, kNameKey, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
        - (NSString *)name {return objc_getAssociatedObject(self, kNameKey);
        }@end

        6.1.3 自动归档与解档

        场景:自动实现NSCoding协议,无需手写每个属性。

        #import <objc/runtime.h>@implementation Person- (void)encodeWithCoder:(NSCoder *)coder {unsigned int count = 0;Ivar *ivars = class_copyIvarList([self class], &count);for (int i = 0; i < count; i++) {Ivar ivar = ivars[i];NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];id value = [self valueForKey:key];[coder encodeObject:value forKey:key];}free(ivars);
        }- (instancetype)initWithCoder:(NSCoder *)coder {if (self = [super init]) {unsigned int count = 0;Ivar *ivars = class_copyIvarList([self class], &count);for (int i = 0; i < count; i++) {Ivar ivar = ivars[i];NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];id value = [coder decodeObjectForKey:key];[self setValue:value forKey:key];}free(ivars);}return self;
        }@end

        6.1.4 消息转发机制

        场景:多重代理、消息中转、容错处理。

        // 快速消息转发
        - (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(testMethod)) {return self.anotherObject;}return [super forwardingTargetForSelector:aSelector];
        }// 完整消息转发
        - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(testMethod)) {return [NSMethodSignature signatureWithObjCTypes:"v@:"];}return [super methodSignatureForSelector:aSelector];
        }- (void)forwardInvocation:(NSInvocation *)anInvocation {if ([self.anotherObject respondsToSelector:anInvocation.selector]) {[anInvocation invokeWithTarget:self.anotherObject];} else {[super forwardInvocation:anInvocation];}
        }

        6.2 Swift RunTime常见用法

        6.2.1 Mirror反射

        场景:调试、日志、自动打印对象属性。

        struct Person {var name: Stringvar age: Int
        }let p = Person(name: "Tom", age: 18)
        let mirror = Mirror(reflecting: p)
        for child in mirror.children {print("属性名: \(child.label ?? ""), 属性值: \(child.value)")
        }
        // 输出:
        // 属性名: name, 属性值: Tom
        // 属性名: age, 属性值: 18

        6.2.2 动态派发与@objc

        场景:需要与OC交互、KVO、KVC、消息转发等。

        class Animal: NSObject {@objc dynamic func run() {print("Animal run")}
        }class Dog: Animal {override func run() {print("Dog run")}
        }let a: Animal = Dog()
        a.run() // 输出 Dog run,走动态派发

        总结

        RunTime(运行时)是iOS开发的核心机制,主要指程序在运行过程中动态管理类型、对象、方法等的能力。Objective-C的RunTime极为强大,支持动态派发、方法交换、动态添加属性、消息转发等,底层通过isa指针、类对象、元类对象、方法缓存等结构实现,所有方法调用都走objc_msgSend,具备极高的灵活性和反射能力,适合AOP、热修复等高级玩法。Swift的RunTime则以类型安全和高性能为主,大部分方法静态派发,只有特殊场景才动态查找,反射能力有限,无法动态修改类型结构。两者对比,OC更动态、灵活,Swift更安全、高效。实际开发中,OC适合底层框架和需要动态性的场景,Swift则适合业务开发和性能敏感场合。理解RunTime原理,有助于开发者写出更高效、灵活和安全的iOS代码。


        如果觉得本文对你有帮助,欢迎点赞、收藏、关注我,后续会持续分享更多 iOS 底层原理与实战经验

        相关文章:

        深度对比:Objective-C与Swift的RunTime机制与底层原理

        1. RunTime简介 RunTime&#xff08;运行时&#xff09;是指程序在运行过程中动态管理类型、对象、方法等的机制。Objective-C 和 Swift 都拥有自己的运行时系统&#xff0c;但设计理念和实现方式有很大不同。理解 RunTime 的底层原理&#xff0c;是掌握 iOS 高级开发的关键。…...

        升级Xcode16,flutter项目报错

        升级xcode16后发现原来的项目运行不了了&#xff0c;flutter的版本是3.3.1 保错&#xff1a;[ 304 ms] Could not build the precompiled application for the device. [ 16 ms] Error (Xcode): linker command failed with exit code 1 (use -v to see invocation) 1、找到本地…...

        MCU内存映射技术详解

        MCU内存映射技术详解 1. 引言 内存映射是微控制器(MCU)系统设计中的核心概念&#xff0c;它决定了MCU如何访问和管理内存资源。通过内存映射&#xff0c;处理器可以将物理设备的地址空间映射到自己的逻辑地址空间中&#xff0c;实现对各种硬件资源的统一访问。本文将深入探讨…...

        Mac搭建Flutter IOS环境详细指南

        目录 1. 准备工作 2. 下载Flutter SDK&#xff1a; 2.1 配置环境 2.2 解决环境报错 zsh:command not found:flutter 2.3、再使用source命令重新加载一下&#xff1a;【source ~/.zshrc】,下次再编辑这个文件就可以直接执行&#xff1a;【open ~/.zshrc】 2.4、执行【flut…...

        计算机基础—(九道题)

        1. 流程控制题 编写一个程序&#xff0c;输入一个整数&#xff0c;判断它是否是素数&#xff08;质数&#xff09;。 如果是素数&#xff0c;输出Yes&#xff0c;否则输出No。 #include <stdio.h> int main() /* 1. 流程控制题编写一个程序&#xff0c;输入一个整数&…...

        蓝桥杯2025年第十六届省赛真题-可分解的正整数

        其实只有1不符合要求&#xff0c;因为-1010&#xff0c;作为对称结构&#xff0c;任何数都可以改成加法&#xff0c;比如4-3-2-101234&#xff0c;但因为最小连续的数为3,1恰好在里面&#xff0c;所以1不行。 #include<bits/stdc.h> using namespace std; #define int l…...

        Linux虚拟机无法重启网络

        这是为什么啊&#xff1f;试了很多种方法都还是失败。 这是本机vmware8的网络配置 这是linux虚拟机的nat配置 这是虚拟机里静态配置的网络 有没有可以答疑解惑一下的&#xff1f;...

        8.Android(通过Manifest配置文件传递数据(meta-data))

        配置文件 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"><applicationandroid:allowBackup"tr…...

        17:00开始面试,17:08就出来了,问的问题有点变态。。。

        从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到4月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…...

        Docker 常用命令(涵盖多个方面)

        Docker 命令完整列表&#xff08;表格形式&#xff09; 类别 命令 描述 示例 Docker 服务管理 sudo systemctl start docker 启动 Docker 守护进程 sudo systemctl start docker sudo systemctl stop docker 停止 Docker 守护进程&#xff0c;需先停止所有容…...

        前缀树(Trie)(字典树)

        做leetcode的时候看到前缀树&#xff0c;听都没听过&#xff0c;后来才知道前缀树就是字典树。之前学过&#xff0c;在OJ项目中用字典树来实现黑白名单限制。浅浅复习一下吧 用字典树来实现黑白名单限制 实现步骤 &#xff08;1&#xff09;定义黑名单 import java.util.Arra…...

        word插入APA格式的参考文献

        word插入APA格式的参考文献并实现交叉引用 1. 直接手写并采用超链接 2. 使用zotero插入参考文献后采用超链接(前提下载zotero和对应的插件) 1. 直接手写 APA格式生成 1. 在需要插入参考文献的地方手写格式&#xff0c;如下。 2. 生成书签 名字随便填的&#xff0c;&#x…...

        n8n部署docker本地化备份和数据持久化和迁移问题

        问题总结&#xff1a; 在一开始的操作中&#xff0c;你遇到的主要问题是 Docker 容器内的文件权限导致了文件无法正确写入和修改&#xff0c;尤其是在复制本地备份文件到容器内时。具体问题表现为&#xff1a; 复制文件后&#xff0c;容器内文件权限错误&#xff1a;你使用 do…...

        绘制板块层级图

        目录 【实验目的】 【实验原理】 【实验环境】 【实验步骤】 【实验总结】 【实验目的】 掌握数据文件读取掌握数据处理的方法实现板块层级图的绘制 【实验原理】 板块层级图&#xff08;treemap&#xff09;是一种基于面积的可视化方式&#xff0c;通过每一个板块&…...

        国标云台控制状态

        1.基本概念 国标联网系统的信息传输、交换、控制方面的都是通过SIP服务器负责通讯得&#xff0c;SIP负责信令流逐级转发。其中最重要的一部分就是和摄像机进行信令交互。 像安全注册、实时视音频点播、历史视音频的回放等应用的会话控制采用IETFRFC3261规定的Register、Invite等…...

        PostgreSQL与MySQL哪个适合做时空数据分析?

        PostgreSQL与MySQL的定位与区别 定位差异&#xff1a;功能导向与性能优先 PostgreSQL和MySQL作为两大主流开源数据库&#xff0c;其核心设计理念和适用场景存在显著差异。PostgreSQL定位为 对象-关系型数据库&#xff08;ORDBMS&#xff09; &#xff0c;强调功能完备性与标准…...

        uniapp利用生命周期函数实现后台常驻示例

        在 Uniapp 中&#xff0c;利用生命周期函数实现“后台常驻”主要是通过监听应用的前后台状态变化&#xff08; onHide 和 onShow &#xff09;&#xff0c;并结合 定时器、后台任务或状态保持逻辑 来实现。但需注意&#xff1a; 纯前端 JS 代码无法突破系统对后台应用的限制&am…...

        JAVA设计模式——(八)单例模式

        JAVA设计模式——&#xff08;八&#xff09;单例模式 介绍理解实现饿汉式懒汉式 应用 介绍 确保一个类只存在一个实例。 理解 就是一个实例&#xff0c;new出来的一个&#xff0c;很简单。不过单例模式分为了懒汉式和饿汉式&#xff0c;其中也有线程安全的实现方式和线程不…...

        【亚马逊云】AWS Wavelength 从理论讲解到实验演练

        一、什么是 AWS Wavelength&#xff1f; Wavelength——一种新型的 AWS 基础设施&#xff0c;旨在运行需要低延迟或边缘弹性的工作负载。 AWS Wavelength 将按需计算和存储服务引入通信服务提供商网络&#xff0c;使客户能够构建和部署满足其数据驻留、低延迟和弹性要求的应用…...

        Uniapp:vite.config.js全局配置

        目录 一、基本概述二、配置自动引入插件一、基本概述 vite.config.js 是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被自动加载,一般用于配置 vite 的编译选项 二、配置自动引入插件 在项目命令行终端中执行如下代码 npm install unplugin-auto-import…...

        Springboot整合阿里云腾讯云发送短信验证码 可随时切换短信运营商

        本文描述了在springboot项目中整合实现对接阿里云 和 腾讯云的短信验证码发送&#xff0c;可通过更改配置文件达到切换短信发送运营商(申请签名、短信模版这些本文不在叙述)。 首先看下大体结构&#xff1a; 一、需要导入的jar <dependency><groupId>com.…...

        git 查看用户信息

        在 Git 中查看用户信息是一项常见的任务&#xff0c;可以帮助你确认当前仓库的配置或全局的 Git 配置是否正确设置。你可以通过多种方式来查看这些信息。 查看全局用户信息 全局用户信息是应用于所有 Git 仓库的默认设置。要查看全局用户信息&#xff0c;可以使用以下命令&am…...

        JAVA基础:Collections 工具类实战指南-从排序到线程安全

        在 Java 开发中&#xff0c;集合类几乎贯穿每一个项目&#xff0c;而Collections工具类提供了一系列强大的方法&#xff0c;用于操作和增强集合的功能。无论是排序、查找还是线程安全的封装&#xff0c;Collections工具类都是提升代码效率和质量的重要工具。 一、Collections …...

        【计算机视觉】TorchVision 深度解析:从核心功能到实战应用 ——PyTorch 官方计算机视觉库的全面指南

        TorchVision 深度解析&#xff1a;从核心功能到实战应用 ——PyTorch 官方计算机视觉库的全面指南 1. TorchVision 项目概览核心模块 2. 实战案例&#xff1a;10 大应用场景详解案例 1&#xff1a;使用预训练 ResNet 进行图像分类代码实现常见问题相关论文 案例 2&#xff1a;加…...

        case和字符串操作

        使用if选择结构 if [];then elif [];then #注意这个地方,java是else if else ; fi 使用for循环结构 使用for循环&#xff0c;语法结构如下所示&#xff1a; for 变量名 in 值1 值2 值3 #值的数量决定循环任务的次数 do命令序列 done#循环输出1到10 for i in {1..10} #注…...

        Golang|外观模式和具体逻辑

        最终返回的是Document的切片&#xff0c;然后取得Bytes自己再去做反序列化拿到文档的各种详细信息。 外观模式是一种结构型设计模式&#xff0c;它的目的是为复杂的子系统提供一个统一的高层接口&#xff0c;让外部调用者&#xff08;客户端&#xff09;可以更简单地使用子系统…...

        关于kafka

        1.为什么需要消息队列 举个经典的例子。 你是一个网购达人&#xff0c;经常在网上购物。快递小哥到了你的小区后&#xff0c;立刻给你打电话说&#xff1a;“你的快递到了&#xff0c;请马上来取。” 但你是一个合格的牛马&#xff0c;在上班&#xff0c;不方便取快递&#…...

        OpenCV 图形API(67)图像与通道拼接函数-----水平拼接(横向连接)两个输入矩阵(GMat 类型)函数concatHor()

        操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数用于水平拼接两个 GMat 矩阵&#xff0c;要求输入矩阵的行数必须一致: GMat A { 1, 4,2, 5,3, 6 }; GMat B { 7, 10,8, 11,9, 12 }; GM…...

        夜莺 v8.0.0-beta.10 部署

        夜莺 v8.0.0-beta.10 部署 1. mariadb-server2. Redis安装3. 下载 n9e-v8.0.0-beta.10-linux-amd64.tar.gz设置 root 用户密码配置文件 配置mariadb的登录密码导入数据库表结构配置为 systemd 启动服务重新加载 systemd配置日志 访问夜莺VictoriaMetrics 时序数据库安装接入数据…...

        HTML5好看的水果蔬菜在线商城网站源码系列模板7

        文章目录 1.设计来源1.1 主界面1.2 关于我们界面1.3 商城界面1.4 商品信息界面1.5 我的账户界面1.6 联系我们界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#…...

        优化问题中变量分类与作用分析

        优化问题中的变量分类与作用 在优化问题中&#xff0c;变量的定义和作用因问题类型和建模需求而异。以下从决策变量、控制变量的区别与联系出发&#xff0c;结合其他相关变量进行系统分析&#xff1a; 1. 决策变量&#xff08;Decision Variables&#xff09; 定义&#xff1a…...

        RSS‘25|CMU提出统一空中操作框架:以末端执行器为中心,无人机实现高精度遥操作

        导读在科技飞速发展的当下&#xff0c;机器人技术不断拓展其应用边界&#xff0c;空中操作领域成为了研究的热点之一。无人空中操纵器&#xff08;UAMs&#xff09;凭借其在高空复杂任务中的巨大潜力&#xff0c;正逐渐改变着诸如高空设备维护、桥梁检测等传统行业的作业模式&a…...

        智能指针之设计模式6

        本系列文章探讨了智能指针和设计模式的关系&#xff0c;前面五篇文章介绍的是使用设计模式实现了智能指针的相关特性&#xff0c;比如使用工厂模式控制了智能指针对象的创建&#xff0c;使用代理模式控制了资源对象的销毁。本文介绍一下使用智能指针来帮助我们实现相关的设计模…...

        【设计模式】GOF概括

        一、创建型模式&#xff08;5种&#xff09; 1. 单例模式 (Singleton) 适用场景&#xff1a;全局唯一实例&#xff08;如配置管理、日志工具&#xff09;。C示例&#xff1a;// 所谓的scott mayer单例模式 class Singleton { public:static Singleton& getInstance() {st…...

        深入浅出限流算法(三):追求极致精确的滑动日志

        在限流的世界里&#xff0c;精度往往是关键。我们已经讨论过固定窗口&#xff08;简单但有突刺&#xff09;和滑动窗口&#xff08;更平滑但仍有格子边界&#xff09;。如果我们需要更精确的控制&#xff0c;滑动日志 (Sliding Log) 算法便登场了。 核心思想&#xff1a;记录每…...

        一文读懂Tomcat应用之 CentOS安装部署Tomcat服务

        目录 一、Tomcat概述 (一)、Tomcat安装目录简介 (二)、Tomcat配置文件简介 1、server.xml文件 2、web.xml 3、context.xml 4、tomcat-users.xml 5、logging.properties 二、Tomcat安装部署 (一)、环境规划 (二)、安装JDK 1、下载JDK二进制安装包 2、解压JDK二进制…...

        JVM 内存分配策略

        引言 在 Java 虚拟机&#xff08;JVM&#xff09;中&#xff0c;内存分配与垃圾回收是影响程序性能的核心机制。内存分配的高效性直接决定了对象创建的速率&#xff0c;而垃圾回收策略则决定了内存的利用率以及系统的稳定性。为了在复杂多变的应用场景中实现高效的内存管理&am…...

        轻松上手:使用 Docker Compose 部署 TiDB 的简易指南

        作者&#xff1a;ShunWah 在运维管理领域&#xff0c;我拥有多年深厚的专业积累&#xff0c;兼具坚实的理论基础与广泛的实践经验。精通运维自动化流程&#xff0c;对于OceanBase、MySQL等多种数据库的部署与运维&#xff0c;具备从初始部署到后期维护的全链条管理能力。拥有Oc…...

        Linux权限管理

        权限的概念 在 Linux 系统里&#xff0c;权限管理是系统安全的关键环节。权限管理的核心目的是明确不同用户对文件和目录的操作许可范围&#xff0c;以此来保障系统资源的安全与合理使用。权限管理涉及三种不同的用户角色和三种基本的操作权限。 用户角色 所有者&#xff08…...

        Crusader Kings III 王国风云 3(十字军之王 3) [DLC 解锁] [Steam] [Windows SteamOS macOS]

        Crusader Kings III 王国风云 3&#xff08;十字军之王 3&#xff09; [DLC 解锁] [Steam] [Windows & SteamOS & macOS] DLC 版本 至最新全部 DLC 后续可能无法及时更新文章&#xff0c;具体最新版本见下载文件说明&#xff1b; DLC 解锁列表&#xff08;仅供参考&am…...

        架构风格对比

        架构风格深度对比&#xff1a;从管道-过滤器到微内核 &#x1f4dc; 引言 在软件架构设计中&#xff0c;不同的架构风格适用于不同的业务场景。本文将深入解析 7种主流架构风格&#xff0c;包括它们的核心思想、优缺点、适用场景&#xff0c;并通过对比表格和示例帮助您选择最…...

        V Rising 夜族崛起 [DLC 解锁] [Steam] [Windows SteamOS]

        V Rising 夜族崛起 [DLC 解锁] [Steam] [Windows & SteamOS] 注意 这个符号表示 可打开折叠内容 需要有游戏正版基础本体&#xff0c;安装路径不能带有中文&#xff0c;或其它非常规拉丁字符&#xff1b;仅限用于自建服务器&#xff0c;并禁用 VAC &#xff01;&#xff0…...

        HTML标记语言_@拉钩教育

        目录 1.文本标签 2.格式化标签 3.图片标签 4.超链接标签 5.表格标签 6表单标签 6.1 6.2 6.3 7.行内框架(超链接内套一个页面) 8.多媒体标签(音/视频) 1.文本标签 2.格式化标签 3.图片标签 4.超链接标签 5.表格标签 6表单标签 6.1 6.2 6.3 7.行内框架(超链接内套一个…...

        云原生开发革命:iVX 如何实现 “资源即插即用” 的弹性架构?

        云原生技术正以惊人的速度重塑软件开发的版图。短短几年间&#xff0c;它从少数技术先驱的实验性方案&#xff0c;迅速崛起为全球企业数字化转型的核心驱动力。Gartner 预测&#xff0c;到 2026 年&#xff0c;全球 85% 的企业将全面采用云原生技术进行应用开发与部署。云原生架…...

        whois为什么有时会返回两个不同的域名状态

        前阵子发现一直想注册但被别人注册了的一个域名快要过期了&#xff0c;就想着写个脚本跑在电脑上&#xff0c;每分钟检查一次域名状态&#xff0c;一旦域名被正式删除&#xff0c;就发封邮件通知我&#xff0c;这样就不用频繁手动检查域名状态了。 写脚本时发现一个有趣的现象…...

        跨境电商店铺矩阵布局:多账号运营理论到实操全解析

        在当今竞争激烈的全球电商市场中&#xff0c;跨境电商店铺矩阵布局已成为卖家脱颖而出的关键策略。本文将深入剖析跨境电商店铺矩阵布局的本质、优势&#xff0c;并提供从理论到实操的全方位指导&#xff0c;助力您在全球市场中开启属于自己的销售新篇章。 一、是什么&#xff…...

        安卓基础(强制转换)

        ​​一、强制转换&#xff08;Type Casting&#xff09;​​ ​​1. 什么是强制转换&#xff1f;​​ 当你想将一个类型的对象转换为另一个类型时&#xff0c;如果它们之间存在继承关系&#xff0c;就需要​​强制转换​​。 ​​注意​​&#xff1a;只有存在继承关系的类型…...

        VS2022+OpenCasCade配置编译

        一、Open CASCADE Technology介绍及安装&#xff08;windows10&#xff09; Open CASCADE Technology&#xff08;简称OCCT&#xff09;是一款开源的 3D CAD/CAM/CAE 软件开发平台&#xff0c;广泛应用于工业设计、工程仿真、制造等领域。开源OCC对象库是一个面向对象C类库&…...

        AIGC重构元宇宙:从内容生成到沉浸式体验的技术革命

        1. 引言 当数字技术掀开人类交互的新篇章&#xff0c;元宇宙正从科幻构想蜕变为现实——这个由虚拟与现实交织的数字宇宙&#xff0c;承载着未来社会的娱乐、工作与社交形态。作为核心赋能技术&#xff0c;AIGC&#xff08;人工智能生成内容&#xff09;正以惊人的创造力&…...

        当所有人都用上先进ai,如何保持你的优势?

        这不再是你能用上openai模型别人只能用文心一言的时候&#xff0c;而是每个人都可以免费用deepseek r1的时代。如今&#xff0c;办公室里每个人都能随时调用deepseek模型&#xff0c;喊一声“帮我写段代码”便轻松解决问题。在这种情况下&#xff0c;单纯“会用AI”已经很难再形…...