在运行时添加具有新协议的新类获得不同的行为

Posted

技术标签:

【中文标题】在运行时添加具有新协议的新类获得不同的行为【英文标题】:Add a new class with new protocol at runtime get different behavior 【发布时间】:2015-05-02 13:03:37 【问题描述】:

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

 - (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 Simulator(i386 / x86_64) 中,NSLog 可以很好地打印 ViewController。在 iPhone5s(arm64) 中,应用程序会崩溃或打印 (null)

我找到的第一个解决方案,添加protocol_getNamelike

 - (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_unretainedlike

 - (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 之间的区别,但无济于事。我希望有人可以解释导致不同行为的根本原因是什么。谢谢。

更新:直接从github下载demo代码RuntimeProtocolIssue

【问题讨论】:

【参考方案1】:

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

这是 ARC 与 Objective-C 运行时相比的一个错误。它已被修复,但我认为任何 iOS 版本都没有变化。

最安全的解决方案是在非 ARC 文件中调用 objc_allocateProtocol() 和 objc_registerProtocol()。您的 unsafe_unretained 修复也应该有效。添加对 protocol_getName() 的额外调用不是可靠的解决方法。

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

【讨论】:

以上是关于在运行时添加具有新协议的新类获得不同的行为的主要内容,如果未能解决你的问题,请参考以下文章

在运行时在 jQuery 中添加类并选择新类

创建具有数组作为参数的新对象时,如何正确添加数组作为字段?

利用java反射实现tomcat运行中添加新类

Java面试

使用 predict() 时,预测变量数相同但行数不同的新数据出错

带有 django-nose 的 Django:两个相同的设置文件,在运行测试命令时具有不同的行为