Tuesday, March 23, 2010

Categories in Static Library

I'm using categories to expand a few classes in my static library, I have a category on UIView that creates an image of the view, and a category on NSPredicate that generates a predicate for looking for words starting with a search string.

This worked fine on the simulator, but CRASHED the iPhone.  I had developed for a week simulator only, and was dismayed to find my iPhone not working.

After some digging I found a few posts on stack overflow that referenced using the -all_load flag, I tried that and it did not work.  Then I noticed that I had another category that DID work, an expansion of set.  The difference was that the expansion of NSSet did not have it's own files.

I then created a dummy class, and put all my categories into that class, after doing this my application worked fine, even though I didn't actually instantiate the class anywhere.

Sample include file (note in one case I included the category interface from another include, in the other I just cut & pasted it in:
//

//  CategoryDummy.h
//  JLFoundation
//
//  Created by Jon Lundy on 3/23/10.
//

#import
#import "UIView_Helper.h"
#import "JLPredicateHelper_NSPredicate.h"

@interface UIView(AnimationHelper)

//
// Create a image of the current view, and give it a frame 
// identical to the current location.  The image view is autoreleased.
// 

- (UIImageView *)createImageOfView;

@end

// A bug seems to cause categories to NOT work if they aren't in a file that
// is explicitly included.


@interface CategoryDummy : NSObject {

}

@end


And for the source file:

//
//  CategoryDummy.m
//  JLFoundation
//
//  Created by Jon Lundy on 3/23/10.
//

#import "CategoryDummy.h"

#import


@implementation CategoryDummy

@end




@implementation NSPredicate(JLPredicateHelper)


+ (NSPredicate *) createSearchPredicate:(NSString*) fieldToSearchOn startingText:(NSString*) startingText
{
NSString *searchString=[startingText stringByAppendingString:@"*"];
NSString *predicateFormat=[[NSString alloc] initWithFormat:@"%@ like[cd] %%@",fieldToSearchOn];
NSPredicate *predicate=[NSPredicate predicateWithFormat:predicateFormat,searchString];
[predicateFormat release];
return predicate ;
}
@end

@implementation UIView(AnimationHelper)



//
// Create a image of the current view, and give it a frame 
// identical to the current location.  The image view is autoreleased.
// 
- (UIImageView *)createImageOfView
{
// First getting a view of the current image.
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
// this function returns an autoreleased image.
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imageView=[[UIImageView alloc] initWithImage :viewImage];
imageView.frame=self.frame;
return [imageView autorelease];
}
@end


Where JLPredicateHelper is:


//
//  JLPredicateHelper_NSPredicate.h
//  JLFoundation
//
//  Created by Jon Lundy on 3/19/10.


@interface NSPredicate(JLPredicateHelper) 

// This routine will create a predicate that searches a table on a certain field for any values
// that start with the same text (case insensitive).  The predicate is returned autorelease.
+ (NSPredicate *) createSearchPredicate:(NSString*) fieldToSearchOn startingText:(NSString*) startingText;

@end

This was all I had to do to get my categories working on the iPhone.  I was even able to remove the -all_load compiler flag.

8 comments:

  1. Wow, I had the same problem, although in my case "-all_load" was no good as my static lib is a wrapper around some other private C/C++ library and I got all sorts of build errors.

    The CategoryDummy hack solved it. I only needed "-ObjC", not "-all_load", as you mention.

    Thanks for posting your find. Saved me a headache!

    Cheers,
    Chris

    ReplyDelete
  2. Thank you very much! You saved me from hours of work!

    ReplyDelete
  3. I'm glad that I've been able to help some people. This was one that took me a long time.

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Thank you very much! You saved my sanity.

    In the end I made small change to your approach in way I am importing all .h files into CategoryDummy.h and all .m files into CategoryDummy.m. I have also removed them from targets, only CategoryDummy.m is associated with targets now.

    In this way it keeps h/m together and not mixed with different categories. I think it is easier to maintain as it does not surprise as much as having all categories implemented in one file and it allows for importing original headers instead of CategoryDummy.h.

    ReplyDelete
  6. Well, this approach partially worked for me :-(

    I have 6 different categories, 3 under a wrapped static lib, and 3 the parent static lib. Oddly enough the 3 under the wrapped static lib worked great using this approach, but the 3 in the parent static lib do not.

    What is going on? Any thoughts what might be happening here?

    I know - I am being completely vague - don't know how to state it any other way.

    ReplyDelete