在运行时使用新协议添加新 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.
遇到这个问题希望能帮助到像我这样的人。
我在我的一个项目中发现了一个奇怪的问题。我的目标是在运行时添加一个带有新协议的新 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.
遇到这个问题希望能帮助到像我这样的人。