最近(查看一些代码)我偶然发现一个奇怪的问题导致了我们程序中的错误。
我们使用的API具有以下实现(我将在Swift中编写,即使原始代码在Objective-C中)
internal class MyUUID: NSUUID { }
这完全没用,因为它总是返回一个空实例。
我打算在我的游乐场粘贴代码以供解释。
例如:创建一个简单的NSUUID就是这样的:
let a = NSUUID()
a.description //this creates a valid uuid
创建MyUUID应该是类似的
let b = MyUUID()
b.description //it returns an instance, but is completely empty.
但它不起作用。
多检查一下,发现NSUUID初始化器创建了一个__NSConcreteUUID
实例,而MyUUID没有创建一个,我尝试做什么并不重要,它不会创建一个合适的UUID。
所以,我的问题:是否有可能创建NSUUID的子实现?
答案 0 :(得分:3)
您的证据会凭经验出现,以回答您自己的问题:这是不可能的。 NSUUID
似乎是一个类集群而不是单个类,这有效地阻止了子类化。
Aaron的另一个想法:
实现一个NSUUID
而不是一个的对象。实施-forwardingTargetForSelector:
并返回NSUUID
的实例。考虑覆盖-isKindOfClass:
,但除非必须,否则最好不要。那么你应该能够将你的课程传递给NSUUID
,就像那些期望他们不知道差异的人一样。
鉴于解决方案依赖于动态消息传递中内置的回退机制,我怀疑没有Swift等价物;但是如果你把你的类定义为Objective-C,那么它应该同样适用于Swift。
答案 1 :(得分:1)
您可以使用class_setSuperclass
在运行时更改MyUUID
的超类。由于类型安全,这种方法在Swift中是非法的,但您仍然可以在Objective-C中进行。
根据您的实际目标,您可以改为使用CFUUIDRef。
根据要求,这是class_setSuperclass
方法的示例。只需将其放入新的单一视图项目中即可。
#import <objc/runtime.h>
@interface MyUUID : NSUUID
- (void) UUIDWithHello;
@end
@implementation MyUUID
- (void) UUIDWithHello {
NSLog(@"Hello! %@", self.UUIDString);
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Make a UUID that you want to subclass
NSUUID *uuid = [[NSUUID alloc] init];
NSLog(@"Initial UUID: %@", uuid.UUIDString);
// Ignore deprecation warnings, since class_setSuperclass is deprecated
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// Change MyUUID to inherit from the NSUUID's hidden subclass instead of NSUUID
class_setSuperclass([MyUUID class], [uuid class]); // [uuid class] is __NSConcreteUUID
// Turn deprecation warnings back on
#pragma GCC diagnostic pop
// Make a new myUUID and print it
MyUUID *myUuid = [[MyUUID alloc] init];
[myUuid UUIDWithHello];
}
@end
请注意,这有点危险。如果任何秘密子类NSUUID
具有其他实例变量,则需要更多内存,而[MyUUID alloc]
将不会请求。当某些事情请求这些实例变量时,这可能会导致崩溃。
要解决此问题,您可以改为实例化MyUUID
实例,如下所示:
NSLog(@"Initial UUID's class: %@", NSStringFromClass(uuid.class));
Class topSecretUUIDSubclass = uuid.class; // __NSConcreteUUID
MyUUID *myUuid2 = [[topSecretUUIDSubclass alloc] init];
[myUuid2 UUIDWithHello];
object_setClass(myUuid2, [MyUUID class]);
基本上这会使myUuid2
成为__NSConcreteUUID
,然后将其更改为MyUUID
。但是,这仅在MyUUID
未添加任何实例变量时才有效。
如果MyUUID
需要添加自己的实例变量,则需要使用+alloc
覆盖class_createInstance()
以为这些实例变量提供额外的内存