I am developing a plugin for an iOS application. I am compiling it into a .a file which is then used by the main xcode project.
So far I have create a category of the UIDevice class in this library. When I run the main project using this library it crashes due to an unrecognized selector
-[UIDevice platform]: unrecognized selector sent to instance
platform is one of the fuinctions I added via the category.
So I thought it wasn't linking those functions at all and added a c function to the same file as the UIDevice category then called it from my code .
This time the main project ran fine... So I thought maybe it was something else i did and removed the C function. But lo and behold it crashed again due to unrecognized selector..
My questions:
Why does xcode ignore the category definition unless I call a function declared in the same file?
Is there an xcode setting i can change to make it include these methods from the UIDevice category regardless of whether I call a function from that file or not?
cheers
Check out Building Objective-C static libraries with categories:
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.
To resolve this issue, the target linking against the static library
must 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.
Important: For 64-bit and iPhone OS applications, there is a
linker bug that prevents -ObjC from loading objects files from static
libraries that contain only categories and no classes. The workaround
is to use the -all_load or -force_load flags.
Source: #albertamg (linking objective-c categories in a static library)
I have had the same problem. A method defined in a category defined in a subproject resulted in an unrecognized selector exception. (In fact, this manifested as inability to specify an UILabel subclass in Interface Builder; the XIB contained the class shown in IB (UILabel or UIView, depending on what I dragged there), rather than the class that I have typed in, and that looked as a weird XCode bug.)
The solution that worked for me has been to use -force_load:
In the left-hand panel, select your main project (the root item). On the right, you will see PROJECT and TARGETS. Select TARGETS.
Go to "Build Settings"(in the upper bar) -- "Linking" -- "Other linker flags" and, assuming your subproject is called XXXXX, add
-force_load ${BUILT_PRODUCTS_DIR}/libXXXXX.a
there (the item has two subitems, Debug and Release, but you click on this compound item so that if affects both Debug and Release).
Note that -force_load works for a single library, and you may need to specify a separate -force_load for each subproject library.
I had this issue and spent near 1 hour to resolve it. Thanks god! it's been done. Methods which are defined should be static type!
Related
I am using a third party framework containing a category on NSData and having a static method dataUsingBase64String: in it.
The framework got linked fine and code builds successfully. But I am getting unrecognized selector sent to class runtime error when this method gets called.
I have also tried adding -ObjC,-all_load flags in OTHER_LINKER_FLAGS of XCode with no luck..
I guess your library is statically linked. A common problem in that is categories are not included or linked. You additionally need to add -all_load to the Other Linker Flags of the target which is using your static library.
Also check the below answers similar to your problem
Answer 1 - NSData Unrecognized selector sent to class
Answer 2 - Objective-C Category Causing unrecognized selector
Answer 3 - "unrecognized selector sent to instance" to a static library despite ObjC flag
I hope this helps
Note: I needed to add framework in this way,
Framework Search Paths instead of adding frameworks to "Linked Frameworks and Libraries" section
Here, somehow the framework was not loaded even after adding-Objc or -all_load.
Finally, -framework in OTHER_LINKER_FLAGS did the trick for me.
Something about it from manpage,
-framework name[,suffix]
This option tells the linker to search for `name.frame-
work/name' the framework search path. If the optional suffix
is specified the framework is first searched for the name
with the suffix and then without (e.g. look for `name.frame-
work/name_suffix' first, if not there try `name.frame-
work/name').
If -Objc does not take effect, you can try to delete your Category from the project and drag it to the project again, and check Target membership.
i create a project with a framework target and a app target.
in the framework target, i create a class called MyButton inherit the UIButton class.
in the app target, i use the stroryboard and put a UIButton on it,now i use the MyButton class in the interface.
the question is when i set framework target's mach-o type with default option Dynamic library ,i can build and run success.
but when i change the mach-o type with Static library option,i can build success but run failed.the error message is "Unknown class MyButton in Interface Builder file."
i am confused with it,anyone can explain it please.
the test demo is here. https://github.com/george-luofz/Test_useFrameworkInXib.git
Elaborating on dasdom's answer, a dynamic framework is effectively a directory which apart from the compiled binary can contain additional resource files like storyboards, xibs, images etc.
A static framework is something fundamentally different - it's a piece of compiled code in intermiedate format that must be eventually linked with the final app executable to become usable. It has no placeholder mechanism for additional resource files and is not distributed in a same manner as a dynamic framework.
A possible (but not very practical) workaround to utilise static framework would be to serialize a binary resource and embed it in code (becoming effectively an array of bytes - a rather huge one most likely). That would become really hard to maintain and require updating the corresponding deserialised array in code every time the binary resource file has changed.
You cannot use Storyboards in a static library.
If I have a class named ClassA in my main project, and I have a sub project built for a static library, in this sub project a class also named ClassA. I'm wonder that I can build and run successfully, how the compiler distinguish the two class?
I think you are talking about how linker works.
The static library is a collection of several relocatable object files with the suffix ".o". The source files in your project are also compiled into a relocatable object files.
When linker works, it will resolve the symbols like ClassA that used in your code. If it finds that in a relocatable object file - let's say rof1.o, it would absorb rof1.o into the executable file. The searching order of relocatable object file when linker try to resolve symbols determines which ClassA is used. As the searching order is non-deterministic to us, you should use a different class name.
BTW if you set the other link flags to '-all_load', which indicates that the linker will try to combine all the relocatable object files into the executable file. Then if there are two or more same symbols, it will show an error "duplicated symbols".
You need to rename one of your classes otherwise compiler throws an error when building your code.
I want to create a static library (actually, a framework, but I know how to do that part) that bundles up code from, among other things, another static library. However, the OBJC_CLASS exports from the original library end up as undefined symbols.
For example, in Xcode 5.1.1 (using default settings/choices at every step, unless otherwise specified):
Create a new "iOS Framework & Library Cocoa Touch Static Library" project named LibA.
Build (for either simulator or a real device, doesn't matter).
Create another new "iOS Framework & Library Cocoa Touch Static Library" project named LibB.
Drag libLibA.a from the LibA products to the Frameworks folder in the LibB project tree.
Drag LibA from the include directory next to the static lib to the top level of the LibB project tree.
Edit LibB.h as shown below.
Build (same target as before).
Create a new "iOS Application" (any type) project named AppC.
Drag libLibB.a from the LibB products to the Frameworks folder in the AppC project tree.
Drag LibB from the include directory to the top level.
Drag LibA from the first project's include directory to the top level.
Verify that LibA appears in the Link Binary With Libraries phase.
In any method of any class the wizard generated (e.g., -[MasterViewController awakeFromNib]), add (void)[[LibB alloc] init].
At the top of the .m file you just edited, add #import "LibB.h".
Build.
Here's the LibB.h promised above:
#import <Foundation/Foundation.h>
#import "LibA.h"
#interface LibB: LibA
#end
I get the following error:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_LibA", referenced from:
_OBJC_CLASS_$_LibB in libLibB.a(LibB.o)
"_OBJC_METACLASS_$_LibA", referenced from:
_OBJC_METACLASS_$_LibB in libLibB.a(LibB.o)
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Looking at the files, the problem is obvious:
$ nm -g libLibB.a
U _OBJC_CLASS_$_LibA
0000031c S _OBJC_CLASS_$_LibB
U _OBJC_METACLASS_$_LibA
00000308 S _OBJC_METACLASS_$_LibB
U _OBJC_METACLASS_$_NSObject
U __objc_empty_cache
The symbols for _OBJC_CLASS_$_LibA and _OBJC_METACLASS_$_LibA are exported as undefined.
I can reference methods, C functions and structs, globals, etc. from LibA. Even categories on Foundation objects (as long as I do the category-dummy trick). It's only the class and metaclass objects that I can't figure out how to export.
Here's what I've tried to fix it:
Turn off Dead Code Stripping (in all three projects).
Add -ObjC as an extra linker flag (in all projects). (This makes no sense for static libs, and all it does is give you a warning error telling you exactly that, but everyone suggests it to me.)
Create an "Exported Symbols File" (for LibB). (This also only makes sense for dynamic libs.)
Pass ${PROJECT_DIR}/libLibA.a as an "Other Linker Flags" (for LibB) instead of adding libLibA as a framework (in case -lLibA is processed differently from libLibA.a).
What I've tried that I still think may be on the right path, but I'm not sure:
Try to figure out appropriate libtool options that have no corresponding settings in Xcode. (I can wrap it in a Makefile, or and Xcode custom build step, if necessary.)
Enable "Perform Single-Object Prelink", then add ${PROJECT_DIR}/libLibA.a to "Prelink libraries". I get warnings about duplicate symbols and then success but with an empty libLibB.a, so obviously there's something else I need to do. I've done this with .dylibs and dynamic Frameworks on OS X, and there wasn't anything else I needed to do there… but never with static libs.
Workarounds that I know about (and I'll use one of these if there's no real solution):
Require that anyone who wants to use LibB also has to add LibA to their project. And, in particular, the pre-built copy of LibA that we provide.
Distribute LibB as source to be included in your project, instead of a static lib and headers.
Manually ar libLibA.a and LibB.o, then ranlib like it's 1999 (although the docs say this doesn't work, it seems to).
(None of these are too terrible for my simple test project, but in real life, this is not an open source project, that LibA is actually 80 different libs from 3 different projects, and some of the LibA code builds fat armv7/armv7s (which means ar doesn't work on it…), and we're planning to do the usual hack of lipo'ing together the simulator and native builds and making a framework out of them, all of which makes things more of a problem.
I think I may have solved it with single-object prelink (basically this means it does an ld -r to build a giant object file, then passes that to libtool), although I'm still not sure, and I don't love the solution. So, I will post what I've got as an answer, but hope someone else comes along with a better answer.
To get single-object prelink to work, you need to (in LibB):
Add libLibA.a as a Framework.
Make sure it does not appear in the Link Binary With Libraries build phase.
Set "Dead Code Stripping" to No.
Set "Don't Dead-Strip Inits and Terms" to Yes.
Set `Perform Single-Object Prelink" to Yes.
Set "Prelink libraries" to ${PROJECT_DIR}/libLibA.a
Set "Preserve Private External Symbols" to Yes.
(The second step is what I was doing wrong earlier…)
Unfortunately, this seems to break the dependency rules completely, so that every build recompiles every .m (and .pch) that's part of the target, even if nothing has changed.
Other than that annoyance, this seems to work for both AppC and my real project just fine.
AppC does not need "Preserve Private External Symbols"; my real project does. I believe this is because one of the third-party libraries does an ld -r with an empty -exported_symbols_list explicitly to "transform all symbols to private_extern. Otherwise, class objects don't end up that way. However, I'm not 100% sure I understand this one.
Adding this to Other Linker Flags appears to work
-force_load $(CONFIGURATION_BUILD_DIR)/libLibA.a
We include "-ObjC"in other linker flags of project settings in Xcode if we include any external libraries or frameworks can any one tell me what is the reason for it and what it does if we include it.
Thanks in Advance..
It's explained pretty well in Apple's QA1490.
Key excerpts:
Objective-C does not define linker symbols for each function (or
method, in Objective-C) - instead, linker symbols are only generated
for each class.
...
To resolve this issue, the target linking against the static library
must 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
Categories are most known use case for this flag (as Apple's QA1490 explains), but they are not the only reason why this flag is needed.
Since Objective-C is a dynamic language which makes things like NSClassFromString() possible, the standard behaviour of C linker of including only actually used symbols (and throwing away all others) does not work, because there is no way to find out whether some symbols are actually used in some indirect ways like concatenating strings then calling NSSelectorFromString or NSClassFromString.
Consider xib/nib files for example: they don't participate in linking stage, yet they may instantiate library classes which are not referred by anything but this nib file. If linker threw away those classes as unused, the nib couldn't load and work properly.
The only way to ensure nothing is lost is to include every Obj-C class from a library at link time, and this flag does that.
-ObjC linker flag causes the linker to load ever object file in the library that defines an Objective-C class or category.
Why to use -ObjC linker flag - Apple Documentation?