Swift基础知识(三)

402 阅读5分钟

Swift 与 Objective-C 中的自省(Introspection)对比


1. 核心概念

  • 自省(Introspection):在运行时检查对象的类型或是否符合特定协议的能力。
  • Objective-C:基于 NSObject 的类体系,主要用于检查类继承关系。
  • Swift:支持所有类型(类、结构体、枚举),不依赖 NSObject,且涵盖协议检查。

2. 类型检查语法与行为

特性Objective-CSwift
检查类继承关系isKindOfClass:(判断类或子类)
isMemberOfClass:(仅判断自身类)
is 操作符(判断类型或子类型)
支持类型NSObject 子类所有类型(类、结构体、枚举)
协议一致性检查conformsToProtocol:is(检查协议类型)
as?(转换并检查协议)
值类型支持不支持(仅类)支持(结构体、枚举)
泛型支持支持(结合泛型类型检查)

3. 具体用法与示例

Objective-C
// 类继承检查
BOOL isView = [obj isKindOfClass:[UIView class]];       // YES(obj 是 UIView 或其子类实例)
BOOL isExactView = [obj isMemberOfClass:[UIView class]]; // NO(obj 必须是 UIView 实例)

// 协议检查
BOOL conforms = [obj conformsToProtocol:@protocol(NSCopying)];
Swift
// 类型检查
let obj: Any = "Hello"
if obj is String { // true
    print("是 String 类型")
}

// 协议检查
protocol Drawable { func draw() }
struct Circle: Drawable { func draw() { } }

let shape: Any = Circle()
if shape is Drawable { // true
    print("符合 Drawable 协议")
}

// 值类型检查
let value: Any = 100
if value is Int { // true
    print("是 Int 类型")
}

// 类型转换 + 检查
if let drawableShape = shape as? Drawable {
    drawableShape.draw()
}

4. 核心区别总结

维度Objective-CSwift
类型体系依赖必须继承自 NSObject不依赖任何基类,支持所有类型
检查范围仅类(包括子类)类、结构体、枚举、协议
协议检查需显式调用 conformsToProtocol:直接通过 isas? 检查
语法简洁性方法调用(冗长)操作符(简洁直观)
泛型支持支持泛型类型检查

5. 高级特性(Swift 独有)

  • 模式匹配:结合 switch 语句进行类型和协议匹配。
    func checkType(_ value: Any) {
        switch value {
        case is String: print("字符串类型")
        case let num as Int where num > 0: print("正整数")
        case is Drawable: print("可绘制对象")
        default: break
        }
    }
    
  • 元类型检查:通过 .Type 获取类型元信息。
    let type: Int.Type = Int.self
    let instance = type.init(10) // 创建 Int 实例
    

6. 使用建议

  • Objective-C:在维护旧项目或与 Cocoa 框架交互时使用,注意仅适用于 NSObject 子类。
  • Swift:在新项目中优先使用 isas?,充分利用其对值类型和协议的支持,提升代码灵活性和安全性。

三、Swift 闭包(Closures)与 Objective-C Block 的对比

1. 内存分配与结构

特性Swift 闭包Objective-C Block
底层数据结构闭包是 捕获上下文的函数,本质是结构体Block 是 封装函数指针的结构体对象
内存位置默认栈分配,逃逸闭包自动提升到堆默认栈分配,需显式 copy 到堆
结构体布局闭包结构体包含函数指针和捕获的上下文数据Block 结构体包含 isa 指针、函数指针、捕获变量等
生命周期管理通过 引用计数(ARC) 管理堆内存手动 copy/release 或 ARC 管理堆内存

2. 变量捕获机制

特性Swift 闭包Objective-C Block
值类型捕获捕获值类型的副本(深拷贝)默认捕获值类型变量的 原始值(需 __block 修饰允许修改)
引用类型捕获捕获引用类型的强引用(需通过捕获列表弱化)捕获对象的强引用(需 __weak 弱化)
可变性支持通过 varinout 参数捕获可变变量__block 修饰符实现变量可变性

3. 函数执行与上下文

特性Swift 闭包Objective-C Block
函数指针闭包的函数指针直接指向编译生成的函数代码Block 的函数指针通过 invoke 成员指向代码
上下文管理闭包通过结构体存储捕获的变量(值或引用)Block 通过结构体的 descriptor 管理捕获变量引用计数
逃逸性处理编译器自动检测逃逸闭包,并生成堆分配逻辑需手动调用 copy 将 Block 从栈复制到堆

4. 底层实现细节

Swift 闭包
  1. 结构体表示

    // 伪代码表示闭包结构体
    struct Closure<T> {
        var function: (T) -> Void  // 函数指针
        var capturedValues: [Any]  // 捕获的上下文数据
    }
    
  2. 内存管理

    • 非逃逸闭包:栈分配,函数返回后销毁。
    • 逃逸闭包:自动复制到堆,由 ARC 管理生命周期。
  3. 捕获列表优化

    • 显式声明 [weak self][unowned self],避免隐式强引用。
    • 编译器生成代码时,捕获列表直接修改闭包结构体的成员引用类型。
Objective-C Block
  1. 结构体表示

    // 伪代码表示 Block 结构体
    struct Block_layout {
        void *isa;                  // 指向 Block 类型(栈/堆/全局)
        int flags;                  // 状态标记(是否被拷贝等)
        int reserved;               // 保留字段
        void (*invoke)(void *, ...); // 函数指针
        struct Block_descriptor *descriptor; // 描述符(引用计数、捕获变量等)
        // 捕获的变量数据...
    };
    
  2. 内存管理

    • 栈 Block:默认创建在栈上,函数返回后失效。
    • 堆 Block:通过 copy 操作复制到堆,由引用计数管理。
  3. 变量捕获

    • __block 修饰的变量会被包装为 Block_byref 结构体,允许跨 Block 修改。
    • 对象类型变量通过 retain/release 管理引用计数(ARC 下自动处理)。

5. 性能对比

维度Swift 闭包Objective-C Block
内存开销更小(结构体直接存储捕获数据)较大(包含 isaflags 等元数据)
执行速度更快(直接函数指针调用)稍慢(需通过 invoke 间接调用)
捕获变量修改灵活(值类型副本独立修改)__block 包装,性能开销较大

6. 总结

  • Swift 闭包

    • 更轻量:基于结构体和值语义,减少内存开销。
    • 更安全:编译时检查捕获列表,避免循环引用。
    • 更高效:栈分配优化 + 直接函数调用。
  • Objective-C Block

    • 更底层:直接操作结构体和引用计数,灵活性高。
    • 兼容性:与 Cocoa 框架深度集成,适合传统代码维护。
    • 显式控制:需手动管理 copy__block 修饰符。

底层核心差异

  • Swift 闭包是 值类型结构体,通过编译优化实现高效捕获和内存管理。
  • Objective-C Block 是 对象,依赖运行时元数据(isadescriptor)管理生命周期和捕获变量。