当一个新的 class 添加到运行时时是否可以得到通知? (Objective C)

Is it possible to get notified when a new class is added to the runtime? (Objective C)

Objective-C 运行时允许分别使用 objc_allocateClassPairobjc_registerClassPair 动态创建和注册 class 对。当一个新的 class 添加到运行时时是否可以得到通知? (我只关心已注册的 Objective-C classes 如果有帮助,未注册的 classes 将是一个奖励。)

如果无法通知,下一个最有效率的事情是什么?大多数 iOS 设备都有严格的计算限制,因此我的选择有限。如果可能的话,我愿意接受一些运行时黑客攻击。我意识到我可以挂钩 +load,但这只适用于 NSObject subclasses,而不是纯 Swift classes。

"PureSwiftclasses?但是pureSwiftclasses不是Objective Cclasses!"你说?再想想。尝试使用运行时自省一个纯粹的 Swift class,您会发现它所包含的远比看上去的要多! "How can one end up having a Swift class being added dynamically?" 好吧,我可以加载一个使用 Swift!

的动态框架

自@SpaceDog 询问以来,这是我正在开发的开发人员实用程序。进一步说明:我自己不知道什么时候会调用 objc_allocateClassPairobjc_registerClassPair,因为我的项目是一个库。

简答:否

长答案:有点。

有两种方法可以解决这个问题。遗憾的是,这两者都需要看门狗线程和轮询。

没有您可以 piggy-back 无需重写符号的同步回调。
(当然,如果可以的话,您还有很多其他解决方案)

  1. 第一个选项,仅使用 public APIs:您可以随时观察 objc_getClassList 的计数变化。

    此解决方案的问题在于,它会将 force-load 所有 class 可以简单地加载到共享 DYLD 缓存中的元素提供给用户 space。这只会发生一次,但这样做会对性能造成相当大的影响。冷启动时间当然不好。

    注意:objc_getClassList的排序顺序不是well-defined(内部依赖unstablehash-table),所以没有好的办法(例如小于 O(N) storage/time) 来确定具体添加了哪个 class。


  1. 第二个选项,可能是更可行的选项,是(ab)使用调试器特定 API gdb_objc_realized_classes.

    这是 NXMapTable(class 名称(const char *) 到 Class 指针(又名 objc_class)。

    注意:NXMapTable 不再包含在 public headers 中,因为这是一个非常旧的版本 OSX(10.4?),因此您需要使用最新的来自 Apple Open Source.

    maptable.h 版本

    用法示例:

    #import "maptable.h"
    
    extern NXMapTable *gdb_objc_realized_classes;
    
    int main() {
        printf("Realized classes: %d\n", NXCountMapTable(gdb_objc_realized_classes));
    
        Class superClass = [NSObject class];
        Class myKls = objc_allocateClassPair(superClass, "TestClass", 0);
        objc_registerClassPair(myKls);
    
        printf("Realized classes: %d\n", NXCountMapTable(gdb_objc_realized_classes));
    }
    

    另请注意:gdb_objc_realized_classes 具有 thread-safety 保证,因此如果您有多个线程可能注册 classes,您可以 运行 进入问题。


这些解决方案可以让您知道何时添加一个class,但不一定是添加的特定class。

最后,如果您担心内存(因为您可能应该考虑 Foundation 框架本身就有 1500 个 classes),一种选择可能是保留类似于 bloom filter 的已知已注册 classes,然后,当 objc_getClassList 的结果发生变化时,找出哪个 class 没有通过该布隆过滤器,并且您拥有最新注册的 class.

然而,作为一种概率数据结构,你会有漏报——例如如果您的 pointer/name 哈希值不够好,一些 classes 可能会漏掉。

祝你好运。