Что требуется для реализации корневого класса Objective-C? - PullRequest
5 голосов
/ 27 августа 2010

Я попробовал этот код:

// main.m
#import <stdio.h>

@interface Test 
+ (void)test;
@end
@implementation Test
+ (void)test
{
    printf("test");
}
@end

int main()
{
    [Test test];
    return  0;
}

с LLVM / Clang без какой-либо платформы, он не скомпилирован с этой ошибкой:

Undefined symbols:
  "_objc_msgSend", referenced from:
      _main in main.o
ld: symbol(s) not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Итак, я добавил libobjc.dylib. Код скомпилирован, но сгенерировал это исключение времени выполнения:

objc[13896]: Test: Does not recognize selector forward::
Program received signal:  “EXC_BAD_INSTRUCTION”.

#0  0x9932a4b4 in _objc_error
#1  0x9932a4ea in __objc_error
#2  0x993212b6 in _objc_msgForward
#3  0x99321299 in _objc_msgForward
#4  0x99321510 in _class_initialize
#5  0x99328972 in prepareForMethodLookup
#6  0x99329c17 in lookUpMethod
#7  0x99321367 in _class_lookupMethodAndLoadCache
#8  0x99320f13 in objc_msgSend
#9  0x00001ee5 in start

Я понял, что для корневого класса требуется некоторая реализация, но я не знаю, что мне делать дальше. Что требуется для создания нового корневого класса? И есть ли какие-либо спецификации для этого?

Ответы [ 4 ]

7 голосов
/ 25 января 2011

Я просто пришел к этому вопросу, потому что у меня был тот же «академический» вопрос. Поработав немного, я обнаружил, что другие ответы на этот вопрос не совсем верны.

Это правда, что в среде выполнения Apple Objective-C 2.0 , вы должны реализовать определенные методы, чтобы ваш код работал. На самом деле существует только один метод, который вам нужно реализовать: метод класса initialize.

@interface MyBase 
+ (void)test;
@end
@implementation MyBase
+ (void)initialize {}
+ (void)test {
 // whatever
}
@end

Среда выполнения автоматически вызовет initialize при первом использовании класса (как объяснено в документации Apple ). Непринятие этого метода является причиной ошибки пересылки сообщений.

Компиляция с clang test.m -Wall -lobjc (или gcc) позволит вам без проблем вызывать тест метода класса. Заставить распределять объекты - это отдельная история. По крайней мере, вам понадобится указатель isa на ваш базовый класс, если вы используете переменные экземпляра. Среда выполнения ожидает, что это будет там.

2 голосов
/ 20 августа 2014

В моей системе (GNUstep на linux + GCC) мне пришлось заменить приведенный выше метод alloc на следующий, чтобы образец работал.Я думаю, что это связано с более новой средой выполнения obj-c (документация о среде выполнения приведена здесь: Справочник по Objective-C из библиотеки разработчика Mac.

+ alloc
{
  return (id)class_createInstance(self, 0);
}
2 голосов
/ 27 августа 2010

Во время выполнения Apple минимальные спецификации довольно легко объяснить: вы должны реализовать каждый метод в протоколе NSObject. Смотрите здесь . Это абсолютно нетривиально . Возможно, вы захотите добавить пару дополнительных функций, таких как +alloc, чтобы иметь возможность создавать экземпляр и т. Д. Во всех платформах Apple есть только два открытых корневых класса: NSObject и NSProxy. На практике нет абсолютно никаких причин для создания корневого класса. Я не уверен, что есть какая-либо документация по этому вопросу от Apple.

Что вы будете делать на практике, это наследовать от NSObject или NSProxy и строить поверх них. Ваш код будет работать, если вы выполните следующее:

@interface Test : NSObject
+ (void)test;
@end

Как указал Тило, это не относится к другим средам выполнения, таким как среда выполнения GNU.

1 голос
/ 16 сентября 2010

Приведенный выше пример прекрасно компилируется с gcc и GNU-runtime.В Objective-C обычно любой класс может быть корневым, просто не имея суперкласса.Если среда выполнения Apple требует чего-то другого, то это специфично для среды выполнения.

Кроме того, есть кое-что специфическое для корневых классов:

Все методы экземпляра также являются методами класса с одинаковой реализацией (если не явнореализовано иначе).Вывод следующего приложения:

#import <stdio.h>

@interface Test
+ alloc;
+ (void)test;
- (void)test;
- (void)otherTest;
@end
@implementation Test
+ alloc
{
  return (id)class_create_instance(self);
}

+ (void)test
{
    printf("class test\n");
}

- (void)test
{
    printf("instance test\n");
}

- (void)otherTest
{
    printf("otherTest\n");
}
@end

int main()
{
    id t = [Test alloc];
    [Test test];
    [t test];
    [Test otherTest];
    [t otherTest];
    return  0;
}

будет:

class test
instance test
otherTest
otherTest

Самая сложная часть при создании нового корневого класса - это +alloc и -dealloc, но, как видно изВ моем примере среда выполнения (в моем случае среда выполнения GNU) может сделать это.Но я не знаю, достаточно ли хорошо выделено время выполнения.Я знаю, что некоторые фонды используют собственный механизм выделения, чтобы скрыть счетчик ссылок от структуры объекта.Я не знаю, делает ли Apple это тоже, и если их среда выполнения уже позаботится об этом.

...