views:

34

answers:

2

Hello,

while creating a library that will be used on several projects, I encountered an error that I was not able to resolve by myself.

The library is composed of several "modules" that each declares its set of classes. The modules declares a header file that references the classes. Each module header is included in the library header, and all of them are copied to the library target.

The "GMData" module defines the ORM layer of the library, it declares a "GMInitializerBase" class, its purpose is to initialize the module. It must be called once in the UIApplicationDelegate.

The "GMModel" module contains the base model for the application (Categories, Articles, ...), It must register itself to "GMData" in order to function properly.

Structure:

<Library Root>
    Library.h
    <GMData>
        GMData.h
        GMInitializerBase.{h,m}
    <GMModel>
        GMModel.h
        GMInitializerBase+GMModel.{h,m}

Contents of Library.h

#import "GMData.h"
#import "GMModel.h"

Contents of GMData.h

#import "... ORM related headers ..."
#import "GMInitializerBase.h"

Contents of GMInitializerBase.{h,m}

#import "... ORM Classes ..."

@interface GMInitializerBase : NSObject {

}

+ (void) bootstrap;
+ (GMInitializerBase*) initializer; // autoreleased instance creator

- (void) setup;
- (void) setupStore:(GMManagerFactory*)factory; // Setup database connection
- (void) setupHelpers:(GMHelperFactory*)factory; // Register helpers (abstract)
- (void) setupManagers:(GMManagerFactory*)factory; // Register managers (abstract)

@end

@implementation GMInitializerBase

+ (void) bootstrap {
    GMInitializerBase* initializer = [self initializer];

    [initializer setup];
}

- (void) setup {
    /* Breakpoint 01 */
    GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
    GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];

    [self setupStore:managerFactory];
    [self setupHelpers:helperFactory];
    [self setupManagers:managerFactory];
}

@end

Contents of GMModel.h

#import "... Base Models files ..."
#import "GMInitializerBase+GMModel.h"

Contents of GMInitializerBase+GMModel.{h,m}

@interface GMInitializerBase (GMModel_Additions)

- (void) setup;
- (void) setupGMModelHelpers:(GMHelperFactory*)factory;
- (void) setupGMModelManagers:(GMManagerFactory*)factory;

@end

@implementation GMInitializerBase (GMModel_Additions)

- (void) setup {
    /* Breakpoint 02 */
    GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
    GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];

    // parent implementation
    [self setupStore:managerFactory];

    // current implementation
    [self setupGMModelHelpers:helperFactory];
    [self setupGMModelManagers:managerFactory];

    // parent implementation
    [self setupHelpers:helperFactory];
    [self setupManagers:managerFactory];
}

- (void) setupGMModelHelpers:(GMHelperFactory*)factory { /* ... */ }
- (void) setupGMModelManagers:(GMManagerFactory*)factory { /* ... */ }

@end

Contents of ProjectAppDelegate.m (located in another project, it includes the library.a and search the "Headers" directory)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[GMInitializerBase initializer] setup];
}

Stops at the first breakpoint (Breakpoint 01) It crashed when in the library:

  • I declare an addition without overloading a method;
  • I declare an addition to a Cocoa class ([NSString toto]) without overloading;

In works when in the test project:

  • I declare an addition to a Cocoa class ([NSString toto]) without overloading;

I didn't try to overload a library class but I assume it will work too.

My problem is the following: I can't get the addition workingm and I need it.

Thanks for reading, thanks for answering.

A: 

Make sure you have the -all_load and -ObjC flags set in the "Other Linker Flags" in the project settings. Categories in a library won't work without them.

Shawn Craver
A: 

In Objective-C, you shouldn't override the method in a category of a class. Say you have

@implementation MyClass
-(void)foo
{
    NSLog(@"%@",@"original!");
}
@end

and later you have

@implementation MyClass (MyCategoryA)
-(void)foo
{
    NSLog(@"%@",@"categoryA!");
}
@end

@implementation MyClass (MyCategoryB)
-(void)foo
{
    NSLog(@"%@",@"categoryB!");
}
@end

Then the result of

MyClass* myInstance=...;
[myInstance foo];

is not reliable, see the discussion in the official documentation. The documentation says it works if you have only one category, but the documentation says at the same time you shouldn't use that feature. So, don't do this.

The sole exception is +load. If a category defines this method, the runtime calls it for each category you define. So, if you want to perform some initialization task per category, +load is the way. Read the official documentation and this blog post by Mike Ash.

Yuji