在运行时使用新协议添加新 class 得到不同的行为

Add a new class with new protocol at runtime get different behavior

我在我的一个项目中发现了一个奇怪的问题。我的目标是在运行时添加一个带有新协议的新 class。我拿出我的部分代码来重现这个问题。

 - (void)viewDidLoad {
      [super viewDidLoad];
      [self registerClass:@"Daidouji"];
      [self protocolInClass:NSClassFromString(@"Daidouji")];
 }

 - (void)registerClass:(NSString *)className {
      Class superclass = (Class)objc_getClass("UIViewController");
      Class newClass = objc_allocateClassPair(superclass, [className UTF8String], 0);
      Protocol *newProtocol = objc_allocateProtocol([@"ViewController" UTF8String]);
      objc_registerProtocol(newProtocol);
      class_addProtocol(newClass, newProtocol);
      objc_registerClassPair(newClass);
 }

 - (void)protocolInClass:(Class)cls {
      unsigned count;
      __unsafe_unretained Protocol **protocols = class_copyProtocolList(cls, &count);
      if (count) {
           NSLog(@"%@", [NSString stringWithUTF8String:protocol_getName(protocols[0])]);
      }
      free(protocols);
 }

在iPhone5(armv7)或iOS模拟器(i386 / x86_64)中,NSLog可以打印ViewController .在 iPhone5s(arm64) 中,应用程序将崩溃或打印 (null)

我找到的第一个解决方案,加protocol_getName like

 - (void)registerClass:(NSString *)className {
      Class superclass = (Class)objc_getClass("UIViewController");
      Class newClass = objc_allocateClassPair(superclass, [className UTF8String], 0);
      Protocol *newProtocol = objc_allocateProtocol([@"ViewController" UTF8String]);
      objc_registerProtocol(newProtocol);

      // add here
      protocol_getName(newProtocol);
      class_addProtocol(newClass, newProtocol);
      objc_registerClassPair(newClass);
 }

但是为什么呢?有什么相关性吗?

第二种方案,从朋友那里找到的,加__unsafe_unretained like

 - (void)registerClass:(NSString *)className {
      Class superclass = (Class)objc_getClass("UIViewController");
      Class newClass = objc_allocateClassPair(superclass, [className UTF8String], 0);

      // add here
      __unsafe_unretained Protocol *newProtocol = objc_allocateProtocol([@"ViewController" UTF8String]);
      objc_registerProtocol(newProtocol);
      class_addProtocol(newClass, newProtocol);
      objc_registerClassPair(newClass);
 }

再一次,为什么?

我试图在 objc 运行时源代码中找到 arm64 / non-arm64 之间的区别,但无济于事。我希望有人能解释导致不同行为的根本原因是什么。谢谢。

更新:直接从githubRuntimeProtocolIssue

下载演示代码

最后,我post把这个问题发到苹果论坛了。 很高兴收到苹果回复,

This was a bug in ARC versus the Objective-C runtime. It has been fixed but I don't think any iOS release has the change yet.

The safest solution is to call objc_allocateProtocol() and objc_registerProtocol() in a non-ARC file. Your unsafe_unretained fix should also work. Adding an extra call to protocol_getName() is not a reliable fix.

遇到这个问题希望能帮助到像我这样的人。