5

I tried this code:

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

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

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

with LLVM/Clang without any framework, it doesn't compiled with this error:

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)

So I added libobjc.dylib. Code compiled, but threw this runtime exception:

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

I realized some implementation required for root class, but I don't know what should I do next. What's required to make a new root class? And is there any specification for this?

skaffman
  • 398,947
  • 96
  • 818
  • 769
eonil
  • 83,476
  • 81
  • 317
  • 516
  • I found a valuable resource: http://code.google.com/p/purefoundation/ However, still don't know about specifications. – eonil Aug 27 '10 at 08:41
  • Just for later reference; Apple's open-sourced objc runtime source contains `NSObject` source too. – eonil Dec 14 '12 at 15:39

4 Answers4

7

I just came to this question because I had the same "academic" question. After working through it a bit, I have found that the other answers to this question aren't completely correct.

It is true that on the Apple Objective-C 2.0 runtime, you must implement certain methods in order for your code to work. There is actually only one method that you need to implement: the class method initialize.

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

The runtime will automatically call initialize when you first use your class (as explained in Apple's documentation). Not implementing this method is the reason for the message forwarding error.

Compiling with clang test.m -Wall -lobjc (or gcc) will allow you to call the class method test without any issue. Making object allocation work is a different story. At the very least, you'll need an isa pointer on your base class if you're using instance variables. The runtime expects this to be there.

wbyoung
  • 22,383
  • 2
  • 34
  • 40
2

In my system (GNUstep on linux + GCC) I had to replace the above alloc method with the following, to make the sample work. I think this is due to a newer obj-c runtime (documentation of the runtime here: Objective-C Runtime Reference from the Mac Developer Library.

+ alloc
{
  return (id)class_createInstance(self, 0);
}
2

On the Apple runtime, the minimum specs are pretty easily explained: You have to implement every method in the NSObject protocol. See here. This is absolutely non-trivial. You might want to add a couple of additional functions like +alloc in order to be able to create an instance, etc. There are only two public root classes in all of Apple's frameworks: NSObject and NSProxy. In practice, there is absolutely no reason to create a root class. I'm not sure there is any documentation to this issue by Apple.

What you will do in practice is to inherit from NSObject or NSProxy and build on top of them. Your code will work if you do the following:

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

As Tilo pointed out, this is not the case on other runtimes like the GNU runtime.

Max Seelemann
  • 9,344
  • 4
  • 34
  • 40
  • You are right Max: Implementing a new root class is challenging and can be interesting from an academical point of view, but practically there are only very few applications. – GorillaPatch Aug 27 '10 at 10:04
  • In reality Apple has created only 5 root classes in all Cocoa frameworks, including nsobject and nsproxy. I never had the need to create one on my own. – Max Seelemann Aug 27 '10 at 10:21
  • It was just a kind of trial making objc program on non-Apple platform (like FreeBSD). I agreee with there's no reason to make my own root class in Apple platform :) – eonil Aug 27 '10 at 10:59
  • There are different root classes on different platforms. Most other distributions either include `NSObject` or feature a root class called `object`. YOu might want to google for that... – Max Seelemann Aug 27 '10 at 11:06
  • 3
    This answer is simply wrong. The above example compiles fine with gcc and GNU-runtime. In Objective-C normally any class can be a root class by simply not having a super class. If the Apple-runtime requires something different, then it's runtime specific. – Tilo Prütz Sep 16 '10 at 12:40
  • 1
    @Tilo. I find your response a bit drastic, but yes, I assumed the Apple runtime. As assumed he was wanting to target that runtime, since he ran it on there. Is the gnu runtime still being used anyways? – Max Seelemann Sep 16 '10 at 15:35
  • @Max: Of course it (the GNU runtime) is :). And maybe I was drastic but I think it should be clearly said that there is a difference between the language Objective-C as understood and processed by the compilers and the different runtimes. And if you'll edit your answer to point this one out I'll take back my down vote :). – Tilo Prütz Sep 16 '10 at 20:10
  • 1
    And back to the topic: Is the NSObject protocol part of the Apple runtime or of the Foundation Framework? If the latter the runtime should not expect a root class to implement it. – Tilo Prütz Sep 17 '10 at 10:57
  • 1
    It is part of the framework but it required by the runtime. In fact, a lot of functionality of the runtime is linked to the framework. I'm pretty sure it wouldn't make too much sense to use either separately. – Max Seelemann Sep 17 '10 at 16:40
  • 1
    Woah! I think that's bad (or at least suboptimal ;)) design. IMHO if framework and runtime require each other they should be put together. At this point the GNU runtime is better. It brings a minimum amount of classes to start with and does not rely on the architecture of a specific framework build on top of it. – Tilo Prütz Sep 22 '10 at 09:24
  • Yep, see this the same way. Theoretically one could simply move a few headers and classes out of the framework into the runtime and that'd be fixed... – Max Seelemann Sep 22 '10 at 11:34
1

The above example compiles fine with gcc and GNU-runtime. In Objective-C normally any class can be a root class by simply not having a super class. If the Apple-runtime requires something different, then it's runtime specific.

Additionally there is something specific with root classes:

All instance methods are also class methods with the same implementation (if not explicitly implemented otherwise). The output of the following app:

#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;
}

would be:

class test
instance test
otherTest
otherTest

The hardest part on creating a new root class is the +alloc and -dealloc but as seen in my example the runtime (in my case the GNU-runtime) can do this. But I don't know if the runtime allocation is good enough. I know that some foundations use an own allocation mechanism to hide the reference counter from the object structure. I don't know if Apple does this too and if their runtime already takes care of it.

Tilo Prütz
  • 1,766
  • 3
  • 16
  • 27