Какво е необходимо за прилагане на коренния клас на 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

Разбрах, че е необходима някаква реализация за root клас, но не знам какво да правя след това. Какво е необходимо за създаване на нов корен клас? И има ли спецификация за това?


person eonil    schedule 27.08.2010    source източник
comment
Намерих ценен ресурс: code.google.com/p/purefoundation Въпреки това, все още не не знам за спецификациите.   -  person eonil    schedule 27.08.2010
comment
Само за по-късна справка; Източникът за изпълнение на objc на Apple с отворен код също съдържа 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. На практика няма абсолютно никаква причина да се създава root клас. Не съм сигурен, че има някаква документация за този проблем от Apple.

Това, което ще направите на практика, е да наследите от NSObject или NSProxy и да надграждате върху тях. Вашият код ще работи, ако направите следното:

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

Както Tilo посочи, това не е така при други изпълнения като GNU runtime.

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
Това беше просто един вид пробна програма за правене на обекти на платформа, която не е на Apple (като FreeBSD). Съгласен съм, че няма причина да правя собствен root клас в платформата на Apple :) - person eonil; 27.08.2010
comment
Има различни root класове на различни платформи. Повечето други дистрибуции или включват 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 runtime) е :). И може би бях драстичен, но мисля, че трябва ясно да се каже, че има разлика между езика 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
Уау! Мисля, че това е лош (или поне неоптимален ;)) дизайн. IMHO, ако рамката и времето за изпълнение се изискват взаимно, те трябва да бъдат събрани заедно. В този момент времето за изпълнение на GNU е по-добро. Той носи минимално количество класове за начало и не разчита на архитектурата на конкретна рамка, изградена върху него. - person Tilo Prütz; 22.09.2010
comment
Да, вижте това по същия начин. Теоретично човек може просто да премести няколко заглавки и класове извън рамката във времето за изпълнение и това ще бъде коригирано... - person Max Seelemann; 22.09.2010

В моята система (GNUstep на linux + GCC) трябваше да заменя горния метод за разпределение със следния, за да направя примера да работи. Мисля, че това се дължи на по-ново време за изпълнение на 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