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
source to share
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.
source to share