2014-10-23 3 views
1

В последнее время (пересмотр кода) Я наткнулся на странность, которая приводит к ошибке в нашей программе.Может ли 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?

ответ

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().

+0

Можете ли вы дать мне образец этого? – rodrigoelp

+0

Какой подход? –

+0

Подход «objc_setSuperclass»: – rodrigoelp

3

Ваши доказательства будут отображаться эмпирически, чтобы ответить на ваш собственный вопрос: это невозможно. NSUUID представляется кластером классов, а не одним классом, что эффективно предотвращает подкласс.

Альтернативная идея Аарону:

Реализовать объект, который имеет NSUUID, а не что один. Внесите -forwardingTargetForSelector: и верните свой экземпляр NSUUID. Рассмотрите переопределение -isKindOfClass:, но в идеале не делайте этого, если только не нужно. Тогда вы должны пройти свой класс, как если бы это был NSUUID для всех, кто ожидает его, не зная разницы.

Учитывая, что решение зависит от механизма резервного копирования, встроенного в динамический обмен сообщениями, я подозреваю, что нет эквивалента Swift; однако, если вы определяете свой класс как Objective-C, он должен быть одинаково применим к Swift.

+0

Я думаю, что мне больше нравится ваш ответ, но я обновил свой ответ с более подробной информацией. Я думаю, что я обеспечил хороший (хотя и опасный и устаревший) способ подкласса кластера классов - хотя бы второй взгляд на мое решение. –

+0

Если я могу повторно пробудить этот поток: я хотел сделать что-то подобное, где для удобства я хотел, чтобы «описание» UUID было просто UUIDString, а не '' ___ NSConcreteUUID > '. Даже в Swift 2.2 это еще единственный вариант? Я не хочу иметь код Obj-C только ради этого удобства. – BaseZen