35

I have created a static library to house some of my code like categories.

I have a category for UIViews in "UIView-Extensions.h" named Extensions.

In this category I have a method called:

- (void)fadeOutWithDelay:(CGFloat)delay duration:(CGFloat)duration;

Calling this method works fine on the simulator on Debug configuration.

However, if try to run the app on the device I get a NSInvalidArgumentException:

[UIView fadeOutWithDelay:duration:]: unrecognized selector sent to instance 0x1912b0
 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIView fadeOutWithDelay:duration:]: unrecognized selector sent to instance 0x1912b0

It seems for some reason UIView-Extensions.h is not being included in the device builds.


What I have checked/tried

I did try to include another category for NSString, and had the same issue.

Other files, like whole classes and functions work fine. It is an issue that only happens with categories.

I did a clean all targets, which did not fix the problem.

I checked the static library project, the categories are included in the target's "copy headers" and "compile sources" groups.

The static library is included in the main projects "link binary with library" group.

Another project I have added the static library to works just fine.

I deleted and re-added the static library with no luck

-ObjC linker flag is set

Any ideas?


nm output

libFJSCodeDebug.a(UIView-Extensions.o):
000004d4 t -[UIView(Extensions) changeColor:withDelay:duration:]
00000000 t -[UIView(Extensions) fadeInWithDelay:duration:]
000000dc t -[UIView(Extensions) fadeOutWithDelay:duration:]
00000abc t -[UIView(Extensions) firstResponder]
000006b0 t -[UIView(Extensions) hasSubviewOfClass:]
00000870 t -[UIView(Extensions) hasSubviewOfClass:thatContainsPoint:]
000005cc t -[UIView(Extensions) rotate:]
000002d8 t -[UIView(Extensions) shrinkToSize:withDelay:duration:]
000001b8 t -[UIView(Extensions) translateToFrame:delay:duration:]
         U _CGAffineTransformRotate
000004a8 t _CGPointMake
         U _CGRectContainsPoint
         U _NSLog
         U _OBJC_CLASS_$_UIColor
         U _OBJC_CLASS_$_UIView
         U ___CFConstantStringClassReference
         U ___addsf3vfp
         U ___divdf3vfp
         U ___divsf3vfp
         U ___extendsfdf2vfp
         U ___muldf3vfp
         U ___truncdfsf2vfp
         U _objc_enumerationMutation
         U _objc_msgSend
         U _objc_msgSend_stret
         U dyld_stub_binding_helper
Corey Floyd
  • 25,929
  • 31
  • 126
  • 154
  • What, specifically, does the exception message say? – Peter Hosey May 31 '09 at 21:03
  • i found this part of your question interesting `the categories are included in the target's "copy headers" and "compile sources" groups.`.. typically the categories are *not* called by projects containing the said static library.. why should it be included in the `copy headers` section? Apple makes no mention of it in its recently published [tutorial](http://developer.apple.com/library/ios/technotes/iOSStaticLibraries/iOSStaticLibraries.pdf) – abbood Jan 05 '13 at 05:00
  • it's *that* step that fixed my problem – abbood Jan 05 '13 at 05:11

9 Answers9

30

The only solution that worked was to include:

"-all_load"

in other linker flags.

EDIT: Be sure to add this flag to the project including the static library, not to the static library itself.

I know this isn't the correct method, but it is working for now.

It maybe a OS 3.0 issue since this was the work around for Three20 as well.

bentford
  • 33,038
  • 7
  • 61
  • 57
Corey Floyd
  • 25,929
  • 31
  • 126
  • 154
  • Check this answer to another question: http://stackoverflow.com/questions/2906147/what-does-the-all-load-linker-flag-does/2906210#2906210 – Sophistifunk Jul 23 '10 at 04:19
  • 7
    Just wanted to add that you need to add this flag to the project including the static library, not in the static library itself. – Pascal Aug 06 '10 at 11:50
  • Just wanted to THANK YOU! This helped a lot. – Romain Jan 17 '14 at 18:53
14

Unfortunately, due to the what categories work and the dynamic nature of the Objective-C runtime, not everything works well with static libraries. The reason you get this error is that the category implementation in the static library is never actually linked into the executable image because the compiler has no way of knowing that the implementation code will be needed at run-time.

In order to cure this, you can force the linker to copy object files from a static archive for any and all Objective-C Class and Category images. The downside is that your executable will include image code for classes that you may not be using at all. To get the linker to include the category code, add -ObjC to the OTHER_LD_FLAGS build setting in Xcode. Your category implementation will now be copied from the static archive to your executable and you won't get the runtime exception.

Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • Well, that's the problem. Did you try running nm on the archive to ensure that the object code is actually making it into the archive? – Jason Coco Jun 01 '09 at 01:59
  • Corey - did you make sure that the -ObjC linker flag is set in *all* configurations and not only in the Debug configuration? – Jason Coco Jun 01 '09 at 02:03
  • ...and did you use nm to check the code is there. I think this is the likely explanation. – Rog Jun 01 '09 at 06:48
  • 1
    Thanks for the suggestions. I did check "all configurations". However, I hadn't used nm to check the Archive (I had to look it up). I ran nm and posted (what I think is) the relevant portion in my question. Your (Jason and Roger) suggestion made me think of a known problem with including Three20 in sdk 3.0 beta. To fix the issue you add another linker flag: "-all_load". This actually solved the problem and the app ran successfully. But I can't help but think this solution has problems (includes code I don't need). Knowing all of this information, what is the "right" way to fix this? – Corey Floyd Jun 01 '09 at 07:56
  • My last comment may be dubious. To be clear: my app is running now, but only with the "-all_load" flag set. – Corey Floyd Jun 01 '09 at 07:57
  • Corey - can you post your project file on pastebin or something and add a link please? Just copy and paste the XML in the file MyProject.xcodeproj/project.pbxproj where MyProject would actually reflect the name of your project. – Jason Coco Jun 01 '09 at 09:07
6

I just spoke to an Apple engineer about this, and this has been addressed in ld with versions >100. This is included in Xcode 4. He walked me through this and I tried it myself and indeed the category problem is fixed.

Take out "-all_load" and go back to "-ObjC" in your Build Settings with the new linker.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
ev0
  • 179
  • 2
  • 7
  • Unfortunately, this still doesn't work in Xcode4. You can get around it by making an empty class in your category file, which works for everything except unit tests. Not even -all_load can make unit tests find your category code unless you associate the file with your test target. – Karl Jun 14 '11 at 19:15
  • 1
    Indeed, it is re-broken in Xcode 4. I have learned of the -force_load flag instead, which does the trick. – ev0 Oct 01 '11 at 04:41
5

If you are on Xcode 3.2 you can avoid using -all_load and instead use -force_load for just the library in question, which should be slightly more efficient.

This is described in a recently updated Apple Technical QA: http://developer.apple.com/mac/library/qa/qa2006/qa1490.html

Erik Doernenburg
  • 2,933
  • 18
  • 21
3

The issue that -all_load or -force_load linker flags were needed to link categories has been fixed in LLVM. The fix ships as part of LLVM 2.9 The first Xcode version to contain the fix is Xcode 4.2 shipping with LLVM 3.0. The mentioned fixes are no longer needed when working with Xcode 4.2. The -ObjC flag is still needed when linking ObjC binaries

tonklon
  • 6,777
  • 2
  • 30
  • 36
  • I am using LLVM 3.0 with XCode 4.2.1 and I still have the issue that categories in static libraries do not work. – Rick Feb 21 '12 at 17:07
  • But you are using the `-ObjC` linker flag? This is and will remain required. Only the `-all_load` flag isn't needed anymore. – tonklon Feb 24 '12 at 15:15
  • yes compiling with the -ObjC flag works. After creating a simple test category the compilation worked. Must have been something weird in the old project file, after adding all the code to a brand new project it all works fine. – Rick Feb 27 '12 at 08:09
2

I just had this same problem but adding any combination of the described flags (-ObjC, -all_load, -force_load) did not work.

It turned out that I had not checked the box "Add to Target" when adding the files to the project. I removed the files from the project and added them again, this time making sure that that box was checked. This fixed the problem.

Matt
  • 21
  • 1
2

I ran into this problem recently. I was unable to get the -all_load to work, when I noticed that another category I had DID work. I was lazy for this category and included it in with another file.

I eventually created a dummy class (no methods, instance variables) and included the implementation of my categories in the .m file for that dummy class. After doing this my categories started working even after I removed the -all_load flag.

This was on iPhone OS 3.1.3.

This certainly is not the RIGHT way to fix it, but it seemed to work.

Full sample code is on my blog for my (trivial) categories.

Jon Lundy
  • 21
  • 1
0

I had the same problem with Categories in my static library. In my case, "-all_load" didn't help as it caused loads of build errors (my static library is a wrapper around another private C/C++ lib).

I solved it by a hack suggested at http://iphonedevelopmentexperiences.blogspot.com/2010/03/categories-in-static-library.html which simply involved adding a dummy (empty) class definition to the category files. Using this hack, I kept "-ObjC" but dropped "-all_load" in the application linker settings and it worked fine on the device.

Chris Miles
  • 7,346
  • 2
  • 37
  • 34
0

In the past I was able to force linkage of the category with -u .objc_category_name_UIView_Extensions, but with the 3.0 dev environment that's broken and the only option seems to be -all_load.