views:

1000

answers:

3

Can you guide me how to properly link static library to iphone project. I use staic library project added to app project as direct dependency (target -> general -> direct dependecies) and all works OK, but categories. A category defined in static library is not working in app.

So my question is how to add static library with some categories into other project?

And in general, what is best practice to use in app project code from other projects?

A: 

Are you #importing the header files that define the categories?

invariant
+1  A: 

You probably need to have the category in you're static library's "public" header: #import "MyStaticLib.h"

christo16
+4  A: 

I found some answers on various forums, blogs and apple docs. Now I try make short summary of my searches and experiments.

Problem was caused by (citation from apple Technical Q&A QA1490 http://developer.apple.com/mac/library/qa/qa2006/qa1490.html):

Objective-C does not define linker symbols for each function (or method, in Objective-C) - instead, linker symbols are only generated for each class. If you extend a pre-existing class with categories, the linker does not know to associate the object code of the core class implementation and the category implementation. This prevents objects created in the resulting application from responding to a selector that is defined in the category.

And their solution:

To resolve this issue, the static library should pass the -ObjC option to the linker. This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.

and there is also recommendation in iPhone Development FAQ:

How do I link all the Objective-C classes in a static library? Set the Other Linker Flags build setting to -ObjC. If that doesn’t bring in all the classes, set it to -all_load.

and flags descriptions:

-all_load Loads all members of static archive libraries.

-ObjC Loads all members of static archive libraries that implement an Objective-C class or category.

-force_load path_to_archive Loads all members of the specified static archive library. Note: -all_load forces all members of all archives to be loaded. This option allows you to target a specific archive.

*we can use force_load to reduce app binary size and to avoid conflicts wich all_load can cause in some cases.

Yes, it works with *.a files added to the project. Yet I had troubles with lib project added as direct dependency. But later I found that it was my fault - direct dependency projecct possibly was not added properly. When I remove it and add again with steps:

  1. Drag&drop lib project file in app project (or add it with Project->Add to project…).
  2. Click on arrow at lib project icon - mylib.a file name shown, drag this mylib.a file and drop it into Target -> Link Binary With Library group.
  3. Open target info in fist page (General) and add my lib to dependencies list

after that all works OK. "-ObjC" flag was enough in my case.

I also was interested with idea from http://iphonedevelopmentexperiences.blogspot.com/2010/03/categories-in-static-library.html blog. Author say he can use category from lib without setting -all_load or -ObjC flag. He just add to category h/m files empty dummy class interface/implementation to force linker use this file. And yes, this trick do the job.

But autor also said he even not instantiated dummy object. Mm… As I've found we should explicitly call some "real" code from category file. So at least class function should be called. And we even need not dummy class. Single c function do the same.

So if we write lib files as:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

and if we call useMyLib(); anywhere in App project then in any class we can use logSelf category method;

[self logSelf];

And more blogs on theme:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html

Vladimir
+1 excellent summary. thanks for reporting back on what you found!
Dave DeLong
The Apple tech note appears to have since been modified to say "To resolve this issue, the target linking against the static library must pass the -ObjC option to the linker." which is the opposite of what's quoted above. We just confirmed that you have to include when linking the app and not the library itself.
Ken Aspeslagh