NSUUID可以通过继承扩展吗?怎么样?

时间:2014-10-23 21:49:53

标签: objective-c xcode swift xcode6 nsuuid

最近(查看一些代码)我偶然发现一个奇怪的问题导致了我们程序中的错误。

我们使用的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的子实现?

2 个答案:

答案 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()以为这些实例变量提供额外的内存