可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答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.
回答3:
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);
}
回答4:
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.