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

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

// 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

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


person eonil    schedule 27.08.2010    source источник
comment
Я нашел ценный ресурс: code.google.com/p/purefoundation. не знаю о спецификациях.   -  person eonil    schedule 27.08.2010
comment
Просто для дальнейшего использования; Исходный код времени выполнения Apple objc с открытым исходным кодом также содержит NSObject источник.   -  person eonil    schedule 14.12.2012


Ответы (4)


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

Это правда, что в среде выполнения 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 на ваш базовый класс, если вы используете переменные экземпляра. Среда выполнения ожидает, что это будет там.

person wbyoung    schedule 25.01.2011
comment
Ты прав. Оно работает! Спасибо, это огромный шаг для меня :) - person eonil; 25.01.2011

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

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

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

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

person Max Seelemann    schedule 27.08.2010
comment
Вы правы Макс: Реализация нового корневого класса сложна и может быть интересна с академической точки зрения, но практических приложений очень мало. - person GorillaPatch; 27.08.2010
comment
На самом деле Apple создала только 5 корневых классов во всех фреймворках Cocoa, включая nsobject и nsproxy. У меня никогда не было необходимости создавать его самостоятельно. - person Max Seelemann; 27.08.2010
comment
Это было своего рода пробное создание программы objc на платформе, отличной от Apple (например, FreeBSD). Я согласен с тем, что нет причин создавать свой собственный корневой класс на платформе Apple :) - person eonil; 27.08.2010
comment
На разных платформах существуют разные корневые классы. Большинство других дистрибутивов либо включают NSObject, либо содержат корневой класс с именем object. Вы могли бы хотеть Google для этого... - person Max Seelemann; 27.08.2010
comment
Этот ответ просто неверен. Приведенный выше пример отлично компилируется с gcc и GNU-runtime. В Objective-C обычно любой класс может быть корневым классом, просто не имея суперкласса. Если среда выполнения Apple требует чего-то другого, то это зависит от среды выполнения. - person Tilo Prütz; 16.09.2010
comment
@Тило. Я нахожу ваш ответ немного резким, но да, я предположил среду выполнения Apple. Как и предполагалось, он хотел нацелиться на эту среду выполнения, поскольку он запускал ее там. В любом случае, среда выполнения gnu все еще используется? - person Max Seelemann; 16.09.2010
comment
@Max: Конечно, это (среда выполнения GNU) :). И, может быть, я был резок, но я думаю, что следует четко сказать, что существует разница между языком Objective-C, как он понимается и обрабатывается компиляторами, и разными средами выполнения. И если вы отредактируете свой ответ, чтобы указать на этот, я заберу свой отрицательный голос :). - person Tilo Prütz; 17.09.2010
comment
И вернемся к теме: является ли протокол NSObject частью среды выполнения Apple или Foundation Framework? В последнем случае среда выполнения не должна ожидать, что корневой класс реализует его. - person Tilo Prütz; 17.09.2010
comment
Это часть фреймворка, но она требуется для среды выполнения. На самом деле многие функции среды выполнения связаны с фреймворком. Я почти уверен, что не имеет особого смысла использовать их по отдельности. - person Max Seelemann; 17.09.2010
comment
Вау! Я думаю, что это плохой (или, по крайней мере, неоптимальный ;)) дизайн. ИМХО, если фреймворк и среда выполнения требуют друг друга, их следует объединить. На данный момент среда выполнения GNU лучше. Он предлагает минимальное количество классов для начала и не зависит от архитектуры конкретной платформы, построенной поверх него. - person Tilo Prütz; 22.09.2010
comment
Ага, смотрите на это так же. Теоретически можно просто переместить несколько заголовков и классов из фреймворка в среду выполнения, и это будет исправлено... - person Max Seelemann; 22.09.2010

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

+ alloc
{
  return (id)class_createInstance(self, 0);
}
person 0xReinventor    schedule 20.08.2014

Приведенный выше пример отлично компилируется с 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 и позаботилась ли об этом их среда выполнения.

person Tilo Prütz    schedule 16.09.2010