My application unit tests build and test when running in the simulator, but fails with a linker error when building and testing to device.
On my application target I've set the following build settings:
DEPLOYMENT_POSTPROCESSING = NO
GCC_SYMBOLS_PRIVATE_EXTERN = NO
On my unit test I've set the following build settings:
BUNDLE_LOADER = $(BUILT_PRODUCTS_DIR)/<app name>.app/<app>
TEST_HOST = $(BUNDLE_LOADER)
The linker error is:
Undefined symbols for architecture armv7s:
"_<An NSString * const>", referenced from:
-[UnitTestClassA setUp] in UnitTestClassA.o
"_<Another NSString * const>", referenced from:
-[UnitTestClassB helperMethod:] in UnitTestClassB.o
-[UnitTestClassB anotherHelperMethod:] in UnitTestClassB.o
ld: symbol(s) not found for architecture armv7s
clang: error: linker command failed with exit code 1 (use -v to see invocation)
... I have "continue after building errors" turned on in Xcode's preferences, but I don't receive a ton of linker errors complaining about NSString * const's. If I'm doing something wrong, then would expect more link errors than the handful I'm getting since I use string constants throughout my production code.
I'm creating my string constants like this:
.h file...
extern NSString * const ReallyGoodString;
.m file...
NSString * const ReallyGoodString = @"This string is great!";
... the .m file is production code, and part of my application target, and so I do not have to link it into the unit test bundle.
So, what is going on here? Why does this work just fine in the simulator and not on device?
I've posted a sample project to Github that illustrates the problem. You can see in the sample project that this problem is inconsistent: some symbols link just fine others do not.
When the linker is creating the Linker-Error
executable, it discards FHKViewControllerThisSymbolWontLink
because nothing in the executable uses it. The linker doesn't know that it should keep the symbol around to be used by the unit test bundle (which is dynamically loaded at runtime).
You can tell the linker not to strip the unused symbol by tagging it with the used
attribute, like this:
NSString * const FHKViewControllerThisSymbolWontLink __attribute__((used)) = @"name";
You will need to do that for each symbol you define that's not used by the main executable but is used by the test suite.
This is most likely because you have symbols that are in use by your test suite that are not used by the main application and Dead Code Stripping is enabled. You can disable the Dead Code Stripping option on a per-configuration basis. I fixed a similar issue to enable tests to run on my device by flipping the option to No for Debug builds only.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With