Skip to content

Understanding load order of classes and categories

Nat! edited this page Mar 4, 2019 · 8 revisions

The runtime loads classes, strings and categories. The addition of a class or category is delayed until all requirements have been met. A category or class may be delayed indefinetely. Strings are added immediately, but they remain unusable until the static string classes (usually NSConstantString and tagged pointer string classes) get loaded.

Class requirements

A class can be added to the runtime only, if the following conditions are met:

  1. Its superclass must be present
  2. The classes and categories enumerated by +dependencies must be present
  3. The protocol classes of the class must be present (except if it IS the protocol class)

+dependencies is a new feature in mulle-objc 0.6.x

Avoid circular dependencies:

@class Foo;
@protocol Foo
@end

@class Bar;
@protocol Bar
@end

@interface Bar <Foo>
@end
@interface Foo <Bar>
@end

Category requirements

A category can be added to the runtime only, if the following conditions are met:

  1. Its class must be present
  2. The classes and categories enumerated by +dependencies must be present
  3. The protocol classes of the category must be present

Specifiying +dependencies

Assume you have a class Foo, which categories a, b. You want the load order to be Foo, Foo(a), Foo(b).

Specify:

@implementation Foo( b)

+ (struct _mulle_objc_dependency *) dependencies
{
    static struct _mulle_objc_dependency   dependencies[] =   
    {
       { @selector( Foo), @selector( a) },
       { 0, 0 }
    };

    return( dependencies);
}

@end

Foo and Foo( a) are sequenced properly already by the requirement, that a class is loaded before its categories are loaded. You want to make sure that Foo( b) appears after Foo( a) though.

Managing +load order

If you can stick a Foundation library and the application in separate shared libraries, any +load method in the application will find the full set of Foundation functionality available. Problems arise, when you can't or don't want to layer your code with shared libraries.

Then a +load in the application may encounter the Foundation in a partially initialized state. Possibly NSAutoreleasePool for example, may not be present yet!

What to expect, when you are inside +load of a class

  1. Your class exists and can be messaged
  2. All super classes exist and can be messaged (but they may not have their categories yet!)
  3. All protocol classes exist and can be messaged (they should not have categories anyway)
  4. All classes and categories listed in +dependencies exist and can be messaged

Everything else may not be there or may not be initialized. For example a constant NSString like @"VfL Bochum 1848" may not be usable yet, unless you put { @selector( NSConstantString), 0 } into the list returned by +dependencies.

What to expect, when you are inside +load of a category

  1. Your class exists and can be messaged
  2. All super classes exist and can be messaged (but they may not have their categories yet!)
  3. All protocol classes exist and can be messaged (they should not have categories anyway)
  4. All categories and classes listed in +dependencies exist and can be messaged.

Tips

  1. If you code a +load function and you stick with C, you should have no problems.
  2. If you need to use Objective-C, do not declare dependencies on abstract classes like NSString. Use concrete classes like 'NSConstantString".
  3. It's a good idea to check for stuck classes and categories at the start of main():
    #if defined( __MULLE_OBJC__) && defined( DEBUG)
    mulle_objc_check_runtime();
    #endif
    
  4. It is a good idea to create a "Foundation" class, that depends on all other categories and classes in the Foundation. That way a user only need to specify his +dependencies only on this Foundation class to have a complete Foundation ready before his +load is run. The MulleFoundation will do this.