10

Regarding this technical Q&A from Apple: http://developer.apple.com/library/mac/#qa/qa1490/_index.html

I think the compiler could mark calls to methods defined in categories at compile-time (it knows that they were defined in a category and not the main class because the prototype was in an @interface Class (Category) section) - so it could build a table in the object files of "external category methods". Then the linker, after doing its normal linking, should be able to concatenate/merge and process the "external category methods" tables from all objects and look for matching symbols in matching class categories from all the linked frameworks/libraries/objects, then it can pull in the ones that weren't already 'in' the target.

There must be something I'm missing, but what is it? Why is this not possible?

jhabbott
  • 18,461
  • 9
  • 58
  • 95

1 Answers1

15

The linker treats static libraries like a big old collection of random pieces from which it will draw individual pieces to fulfill any symbol requests from the rest of the link unit.

I.e. if the main program calls _foo and _foo only appears in the static library, then _foo, along with any dependent symbols, will be dragged in.

However, when you call a method in a category, there is no specific symbol reference due to the dynamism of Objective-C.

The -ObjC flag tells the linker that, because of this, it should grab all categories from the static library and drop 'em into the main binary.


It is a bit confusing and the assumption is that the compiler should be smarter about this (and, assuredly, it should give assistance at the dev tools level). It is important to remember a few things:

  • Anything declared in a header file is pretty much lost by the time the linker rolls around. Symbols are created by compilation units, not by header files. Header files pretty much generate a promise that a symbol will be concretely created later or fulfilled by the link, but cannot create a symbol in and of themselves (or else everyone compilation unit -- every .o -- would end up with a copy of the symbol and hilarity would ensue at link time).

  • Objective-C is fully dynamic. When you say [(id)foo bar];, the only requirement is that bar is defined somewhere prior. Doesn't matter if it is actually implemented at all (until runtime anyway).

  • Categories don't have to have corresponding @implementations; a category can be used to declare that methods might exist and, in fact, prior to adding @optional to @protocol, it was common to use a category on NSObject (ewwwwww) with no @implementation to say "Hey, this optional method might exist at runtime".

  • Compilation and linking are completely separate processes. Compilation is all about expanding the code and turning it into libraries of executable bytes. Linking is all about taking those libraries and putting them together into something that can actually be run, including resolving all the dependencies between libraries. The compiler doesn't really know about how something might be linked and the linker doesn't have any information about where things (that didn't yield hard symbols) might have been defined.

End result?

The linker doesn't have enough information to resolve the dependencies.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • But the compiler knows that it saw a call to a method that was defined in a category, so it *could* create a specific symbol reference to be used at link-time - this is what my question is about. I want to understand this a bit deeper than what's already described in the Technical Q&A I linked. – jhabbott Jun 18 '12 at 23:06
  • Great answer - one question. What is enough to cause the complier to link a symbol from a static lib. Just `#import` or just defining a variable `MyClass c;`, or do you have to call a method on your class `[c myMethod]`? What about if the class is just referenced in interface builder? – Robert Oct 31 '13 at 17:59
  • 1
    @Robert the linker needs to see a symbol that is resolved by hard linking with the library. If it is a symbol resolved dynamically at runtime, it won't count (like IB). It doesn't necessarily have to be a method call, but that is the easiest mechanism to use. I.e. `[MyClass class];`, for example. – bbum Oct 31 '13 at 18:05