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

In one of my projects, I found a strange problem. My goal is to add a new class with a new protocol at runtime. I have reproduced part of my code to reproduce this problem.

 - (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);
 }

      

On iPhone5 (armv7) or iOS Simulator (i386 / x86_64) NSLog can print ViewController . On iPhone5s (arm64) the app crashes or prints (null) .

First solution found, add name_code_name , for example

 - (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);
 }

      

but why? Is there a correlation?

The second solution I found from a friend of mine adds __ unsafe_unretained as

 - (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);
 }

      

Once again, why?

I tried to find the difference between arm64 / non-arm64 in the source code of the objc executable but to no avail. I hope someone can explain what the root cause is doing different behavior. thank.

update: direct download of demo code from github RuntimeProtocolIssue

+3


source to share


1 answer


Finally, I am posting this question to the forums on apples. Glad to get apple's answer

This was a bug in ARC compared to the Objective-C environment. It's fixed, but I don't think the iOS release has changed yet.

The safest solution is to call objc_allocateProtocol () and objc_registerProtocol () on a non-ARC file. Your unsafe fix should work as well. Adding an extra call to the getName () protocol is not a reliable solution.



Wish to help someone like me when you encounter this problem.

+4


source







All Articles