最近(回顾一些代码),我偶然发现了一个奇怪的问题,导致我们的程序中出现了一个bug。
我们正在使用的API有以下实现(我将用Swift编写它,即使原始代码在Objective中)
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的子实现?
发布于 2014-10-23 22:04:00
您可以使用class_setSuperclass在运行时更改MyUUID的超类。由于类型安全,这种方法在Swift中是非法的,但是您仍然可以在目标-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()为这些实例变量提供额外的内存。
发布于 2014-10-23 22:19:42
你的证据会以经验的方式回答你自己的问题:这是不可能的。NSUUID似乎是一个类集群,而不是单个类,这有效地防止了子类化。
亚伦的另一个想法是:
实现一个具有NSUUID的对象,而不是那个对象。实现-forwardingTargetForSelector:并返回您的NSUUID实例。考虑覆盖-isKindOfClass:,但最好不要这样做,除非您必须这样做。然后,您应该能够将您的类作为一个NSUUID传递给任何期望它的人,而他们却不知道它们之间的区别。
考虑到解决方案取决于动态消息传递中内置的回退机制,我怀疑没有Swift等效;但是,如果您将类定义为Objective,那么它应该在Swift中同样可用。
https://stackoverflow.com/questions/26538056
复制相似问题