I have an app that works with and without the linker flag. However, without the linker flag, I get a very different behaviour when adding data to a view.
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.
From this Technical Q&A
Related
I've trawled through questions here on SO looking for any hints to why I'm seeing this behaviour, and nothing yet.
Consider a class (actually two classes exhibiting the same problem), built into a static library, wrapped in a framework bundle (steps used). They inherit from Foundation framework class clusters (NSMutableDictionary and NSMutableArray).
The use of these classes relies on some static variables being initialised before a static function (not class method!) is used to allocate and initialise an instance (a kind of factory helper function I guess?).
When an iOS app project links to that framework there is a difference the Objective-C runtime class loading behaviour between the Simulator and the Device.
Specifically, on a device (iPhone 4, iOS 4.3.3) when the app is loaded these classes do not get a +load message, and the static vars do not initialize, therefore the static factory method fails. On the Simulator, the messages are sent, and all works as intended. Could it be a problem with the Device runtime having a
My question is, can my framework be configured differently to ensure the +load messages are sent? Or have I run into a bug with static library/framework class loading in iOS?
The classes are from the JSONKit library (JKArray, JKDictionary).
An example project that illustrates this problem is here – https://github.com/ohhorob/JSONKit-in-framework-demo
EDIT: As per #bbum's suggestion, I've verified that the JKDictionary and JKArray classes are in fact loaded and available while the application is running. The DeviceBroken branch on the GitHub project is updated with the verification used.
I filed a bugreport (#9461567) with Apple.
The +load methods are not called because you did not actually create a static library but a Relocatable Object File bundle. If you create the static framework with either make-fmwk or the iOS Universal Framework template then the load methods will be called as expected.
Odd; I'd do an NSLog(#"klassy klass %#", [MysteryClass class]); and make sure the classes are actually loaded (but see below -- this may "fix" the problem).
If they are, then this is a bug in the DYLD loader and please file it.
If not, then it is likely that the linker is stripping the class(es) because nothing references them directly. Try adding [MysteryClass class] in the app's applicationDidFinishLaunching: method (doesn't really matter where or, even, if it gets executed... but that'll be an obvious spot).
Also, I'd suggest not using +load and, instead, writing a constructor function. I.e.:
__attribute__((constructor))
static void initLibrary()
{
....
}
If this is a linker issue, that may or may not fix the problem. It is, however, much clearer as to your intentions than the rather magical +load method.
I am new in iphone i am create application with static library. I am create static library and use this but when we launch the app in device this is give error -objc class refrence. i am not recognize how to solve this
have you linked the library in the project settings? check the field library search paths and put the path to the library there. check this for target settings also. Plus add the library to frameworks via right click->add->existing files.
I've had to add the -all_load linker flag to avoid problems with libraries that use categories on externally defined classes.
Here's some more information on it.
What does the -all_load linker flag do?
Is it possible to build a static library, called, say, libA that:
Contains code that calls upon classes from libB (not created by me, so I can't modify or access it);
Will compile when put into an app that doesn't link to libB (naturally, it will crash if the code using libB's classes is called without libB, but I can get around that. I just need the app that links with libA but not libB to compile.)?
Short answer, no.
Static references need to be resolved at link time - that's what static linking means.
What you could do is to build a dynamic library that statically links libB, and then dynamically link it from your application. If you prefer static linking, you could build a static library that does the dynamic linking to your dynamic-linked library.
There is a way, but it's not portable because it relies on features that are not common to all object code formats. It may also rely on knowing the name mangling rules of your compiler. Consider your other options before deciding to do this.
You can include in your library an undefined weak symbol for every libB symbol you reference. If your code is not linked against libB in the final linking step, every undefined weak symbol will be NULL. Otherwise, the undefined weak symbols will be superseded by the symbols from libB. If you're using GCC, you can use __attribute__((weak)) on symbol declarations.
Starting with an app already in development, I have carried out the instructions in the iPhone Development Guide – Unit Testing Applications
I can successfully include and use my App's classes in Application-style tests that run on the device, and output their results to the console.
If I add the following line of code:
STAssertTrue([viewController isKindOfClass:[LoginViewController class]], #"Top view controller is not LoginViewController");
The following build error is generated:
Undefined symbols:
"_OBJC_CLASS_$_LoginViewController", referenced from:
__objc_classrefs__DATA#0 in LoginViewTest.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
I can provide more configuration information for the project and the Testing target, but the setup works file without the [LoginViewController class] line in the test source.
Without that line, I can reference the class, use it's properties and send it messages successfully.
Is there a linking build setting, or bundle loading option that is required when attempting to use an App class in this fashion? Or should I find another type of test to confirm that the class of an object is the expected one?
I have found an answer, but I think there must be a "better" way?
In the build configuration for the Unit Tests bundle, you can specify the Bundle Loader (BUNDLE_LOADER) setting that points to your "host" App.
${TARGET_BUILD_DIR}/AppName.app/AppName
The Unit Tests bundle is being built as a dependency of your App's unit testing target (say, AppName Testing), and as a result I haven't been able to resolve the App executable any better than the above.
The net result is a working unit test injected into the Testing target without any linker errors. The tests run and access the classes as expected.
EDIT: Bundle Loader – Target Configuration
It's important to configure the target that is "hosting" the testing bundle to not hide it's symbols.
GCC_SYMBOLS_PRIVATE_EXTERN = NO
a.k.a. "Symbols hidden by default". From the documentation:
When enabled, all symbols are declared
'private extern' unless explicitly
marked to be exported using
'__attribute__((visibility("default")))' in code. If not enabled, all symbols
are exported unless explicitly marked
as 'private extern'.
For more information, see
http://developer.apple.com/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html.
I just answered this over here:
iPhone unit testing: Symbols not found when calling custom code
I suppose one of these should be closed as a duplicate? I'm not reputable enough to do so...
I also followed Apple's iPhone Unit Testing Applications document and saw a linking error similar to the one described in the question when trying to unit test one of my classes.
Looks like any class referenced in your unit test class and so being run from your test target also needs to be added to that test target. To do this, you would right click your RootViewController class and click 'Get Info' (Cmd-i shortcut). On the targets pane, make sure your unit test target (e.g. 'LogicTests', if you've followed the naming in that document) is checked.
Now that class will be compiled with your tests and should be available to your unit test. To double check, expand the 'Targets/LogicTests/Compile Resources' node in the 'Groups & Files' browser on the left. This lists all the class files available when building the target and should now include your unit test class together with your class under test.
(Note that you'll need to similarly select all appropriate targets when you create a new application or test class - on the same page of the 'New File...' window when you name the file).
(I'm using XCode 3.2.3 & OS 4.0, by the way).
I have added an extension to NSData (base64 extension), which I kept over a separate infrastructure class lib project. But when i use this method from my main project i am getting an error like this: "-[NSConcreteData encodeBase64]: unrecognized selector sent to instance 0x121e60'".
But if i keep the same class in my main project itself, this will execute with out any issue.
I call this method in the following way:
[dev setToken:[token encodeBase64]];
Please suggest why this is not working if i put the extension in another project. (I am already using some other extensions, eg. for NSDate, like this with out any issue.)
Is this on iPhone OS 3.0? The 3.0 SDK broke the use of -ObjC, but you usually are able to link in categories for a static library by adding the -all_load option to Other Linker Flags within your target application.
The issue is that the metadata necessary to configure a category is usually stripped by the linker because it appears to be dead. If you add the "-ObjC" LDFLAG to your project it will tell the linker to link all the potential ObjC info even if it appears to be dead.