Objective-C переопределить класс - PullRequest
       12

Objective-C переопределить класс

3 голосов
/ 06 сентября 2011

У меня есть код, которым я хотел бы поделиться между iOS и проектом OSX.К сожалению, некоторые классы, хотя функционально относительно схожи (и имеют сильно перекрывающиеся определения интерфейса), являются частями разных иерархий и структур и имеют разные имена.Классы UIKit / AppKit являются ярким примером.

Одним из решений было бы использование директив препроцессора, но это создает много повторяющегося кода:

#if TARGET_OS_IPHONE
  - (UIImage *)getImage; //have to create an implementation for each
#else
  - (NSImage *)getImage; //have to create an implementation for each
#endif;

Я думал об использовании отражения.В Objective-C возможно динамически получить объект Class с использованием NSClassFromString(@"name").Это очень хорошо, но при частом использовании создает некрасивый код.Его также нельзя использовать в заголовочном файле, поскольку он не является константой, поэтому это не работает:

//defined once per project
#if TARGET_OS_IPHONE
  Class NUImage = NSClassFromString(@"UIImage");
#else
  Class NUImage = NSClassFromString(@"NSImage");
#endif;

//usage
- (NUImage *)getImage; //does not compile

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

Можно ли сохранить класс как константу?

Ответы [ 2 ]

3 голосов
/ 07 сентября 2011

Вы должны попробовать что-то вроде:

#if TARGET_OS_IPHONE
typedef UIImage NUIImage; // NUIImage is an alias for UIImage
#else
typedef NSImage NUIImage; // NUIImage is an alias for NSImage
#endif

И затем использовать NUIImage везде, где бы вы использовали NSImage или UIImage.Typedef наследуется от C, поэтому это только стоимость компиляции без влияния на время выполнения (например, NSClassFromString(@"NUIImage") вернет nil при прочих равных условиях).Это заставляет компилятор считать одну вещь псевдонимом другой.Если вы принимаете строгие соглашения C, вы, вероятно, назвали бы это NUIImage_t.


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

Вам не нужно находить решение, которое работает в определениях методов или в любых других определениях, обычно используемых в заголовочном файле;Вы можете просто использовать имя класса там.В Objective-C вы всегда ссылаетесь на объекты по указателю, и все указатели выглядят одинаково для аппаратного обеспечения.Таким образом, использование типизированных указателей в исходном коде является фикцией для программиста (в том числе косвенно через соответствующие предупреждения компилятора), не влияя на сгенерированный двоичный файл.

Пока определение, например,после компиляции не имеет значения, доступны ли ни SomeClass, ни SomeOtherClass во время выполнения:

- (SomeClass *)someMethod:(SomeOtherClass *)argument;

Вы не получите исключение только за то, что объявили метод.

Единственное место, которое вам действительно нужно использовать NSClassFromString, это где вы делаете начальную ветвь относительно того, определен ли тип объекта или нет.Например, вы можете написать следующий класс:

@interface SomeClassThatUsesSomeOtherClass

- (id)init;

@property (nonatomic, readonly) SomeOtherClass *otherClassInstance;

- (void)doSomeTask;
- (SomeOtherClassResult *)doSomeOtherTask;

@end

/* elsewhere */

@implementation SomeClassThatUsesSomeOtherClass

@synthesize otherClassInstance;

- (id)init
{
    // blah blah blah, standard init stuff here

    // switch is made implicitly here, because if we get nil back
    // then we'll end up with nil in 'otherClassInstance' — we're
    // considering the class not existing to be equivalent to any
    // other issue that might cause an instance not to be created
    otherClassInstance = [[NSStringFromClass(@"SomeClass") alloc] init];
    if(!otherClassInstance)
    {
        [self release];
        return nil;
    }

    // blah blah blah, rest of standard init
}

// you'll need a suitable dealloc, too

- (void)doSomeTask
{
    [self.otherClassInstance doAThing];
    [self.otherClassInstance doAnotherThing];
}

- (SomeOtherClassResult *)doSomeOtherTask
{
    return [self.otherClassInstance doAThirdThing];
}

@end

И ваш код полностью безопасен, автоматически работает или не работает в зависимости от того, доступен ли класс.

2 голосов
/ 06 сентября 2011

Вы могли бы (вот как я это делал тогда) использовать preprocessor следующим образом:

#define MY_CLASS_NAME @"MyClassName"

в отдельном файле .h (который можно включить в файл .pch) изатем используйте его так, как вам нужно

Class myClassInstance = NSClassFromString(MY_CLASS_NAME);

:)

...