I'm looking for a way to check if a framework exists and/or if it's classes are defined, before importing and using that framework. Specifically, the framework is Assets Library.
Currently, I'm able to do this with the Core Data framework, since that framework has a file called CoreDataDefines.h which provides a preprocessor directive _COREDATADEFINES_H. This allows me to simply check for that defintion like so:
#ifdef _COREDATADEFINES_H
#import <CoreData/CoreData.h>
// do something with Core Data
#else
// do something without using Core Data
#endif
Unfortunately, the Assets Library does not provide a clear definitions header file so I'm looking for a way to write my own #define statement that can check for the framework's existence before importing it, much like I have done for Core Data above.
I have tried this:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
// import assets library if defined !
#define ASSETSLIBRARY_DEFINE (NSClassFromString(#"ALAsset") != nil)
#if ASSETSLIBRARY_DEFINE
#import <AssetsLibrary/AssetsLibrary.h>
#endif
#endif
... but no luck.
The compiler tells me that the "Token is not a valid binary operator in a preprocessor subexpression."
Any help is always much appreciated.
If you know what class should be imported with the framework you can check if it is loaded:
BOOL isFrameworkLoaded = (NSClassFromString(#"MyClassNameFromTheFramework") != nil);
What you're doing here is very wrong. The _COREDATADEFINES_H define you see in CoreDataDefines.h is known as the sentinal value, an old C technique to avoid multiple inclusions of the same header file.
You should certainly not be using this in your own code, and the presence of the sentinal only tells you that the header has already been included somewhere else. If the sentinal is not defined, it simply means the header has not been included, not that the framework itself is nonexistant.
I'm not sure exactly what you are trying to do, but it looks like you want to use macros to decide between code that uses the framework, and code that doesn't use the framework. If you want to do this at compile time, your only choice is to define your own macros and set them up in your target with some compiler options. For example, to enable code that uses the assets library you might define this in the "Other C Flags" build setting:
-DUSE_ASSETS_FRAMEWORK
And then use this in your code:
#ifdef USE_ASSETS_FRAMEWORK
#import <AssetsLibrary/AssetsLibrary.h>
// code that uses assets framework
#else
// code that does not use assets framework
#endif
If you want to be able to detect at runtime whether the app is linked against the framework and that the framework exists on the current iOS version, you should use the standard approach that Apple recommends which is to test the existance of any classes or functions you need:
if (NSClassFromString(#"ALAsset")) {
// ALAsset is available }
} else {
// ALAsset not available
}
I have a few tricks for this kind of thing… Though what follows may not EXACTLY solve the problems you detailed, this may well aide in finding your ultimate solution… The first "strategy" can be called upon in a "Script Build Phase". This can be used in many ways, but in this example it TESTS that the Framework is valid, (according to otool, and then does some "post-processing" accordingly.. and then tests it again.. and so on..
function test { "$#"; status=$?;
if [ $status -ne 0 ]; then
echo "error with $1"; fi
return $status
}
PRODUCT_PATH="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}"
BINARY="$PRODUCT_PATH/${PRODUCT_NAME}"
MAC="/Library/Frameworks/"
SYSTEM_PRODUCT="$MAC/${WRAPPER_NAME}"
test otool -l "$BINARY" #/Library/Frameworks/AtoZ.framework/AtoZ
if [ $status -ne 0 ]; then
sudo rm -r "$SYSTEM_PRODUCT"
sudo cp -r "$PRODUCT_PATH" "$SYSTEM_PRODUCT"
test otool -l "$BINARY"
if [ $status == 0 ]; then
cd "${PRODUCT_PATH}"
ln -sF Versions/Current/Frameworks Frameworks
fi
The next hand-me-down in this wisdom free-for-all is the sexy, and aptly named NSBundle method.. preflightAndReturnError
NSBundle *b = [NSBundle bundleWithPath:path];
NSError *e = nil;
BOOL okdok = [b preflightAndReturnError:&e];
if (okdok)
okdok = [b load];
if (!okdok) { [self jumpOffABridge:nil]; goto hell; }
Chances are though… you'll be.. OK DOK, by the time load finishes up.
Related
Summary:
Error I'm experiencing is as title states.
I'm encountering a Reference to '' is ambiguous error. I've been unable to determine what the problem, whether if my import structure is set up incorrectly and the headers are somehow included multiple times or whatnot.
Due to request and requirements, I'm currently trying to build a single view iOS application using Objective-C and C while importing a custom pure C SDK. This is my first time using C in an iOS application, let alone including a whole SDK, so I'm not 100% sure if the process I'm doing this is correct, but have been able to resolve errors as I've been compiling.
SDK files are being included via a prefix.h which is imported into the single viewcontroller's header. I also read somewhere to use .pch instead of .h, but I'm not able to figure out how to do so given the requirements of this project...
Errors:
Reference to 'E_EXTERNAL_DEVICE_TYPE__CARD_ENTRY_DEVICE' is ambiguous.
"In file included from .../ViewController.m"
-"In file included from .../ViewController.h"
"In file included from .../prefix.h"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_TYPE__CARD_ENTRY_DEVICE'"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_TYPE__CARD_ENTRY_DEVICE'"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_TYPE__CARD_ENTRY_DEVICE'"
Reference to 'E_EXTERNAL_DEVICE_CONNECT_TYPE__BLUETOOTH' is ambiguous.
"In file included from .../ViewController.m"
"In file included from .../ViewController.h"
"In file included from .../prefix.h"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_CONNECT_TYPE__BLUETOOTH'"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_CONNECT_TYPE__BLUETOOTH'"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_CONNECT_TYPE__BLUETOOTH'"
Reference to 'E_EXTERNAL_DEVICE_TYPE__PRINTER' is ambiguous.
"In file included from .../ViewController.m"
"In file included from .../ViewController.h"
"In file included from .../prefix.h"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_TYPE__PRINTER'"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_TYPE__PRINTER'"
"Candidate found by name lookup is'_E_EXTERNAL_DEVICE_TYPE__PRINTER'"
// Device.c
...
// this and other similiar lines of code that throws the error
if((deviceType == E_EXTERNAL_DEVICE_TYPE__CARD_ENTRY_DEVICE) && E_EXTERNAL_DEVICE_CONNECT_TYPE__BLUETOOTH) {
CODE x = E_EXTERNAL_DEVICE_TYPE__PRINTER;
}
Solutions attempted
The strange part is, at one point I was able to build and run parts of the project by interatively uncommenting lines and debugging lines of code as I went along starting with SDKInitialize. But for some unknown reason, I'm now getting this error even though I've commented the SDKInitialize() in ViewController.m such that it's just an empty ViewController doing nothing.
I've also tried reverting back an older git version where the project could build fine, but it's still encountering the same error, which possibly leads me to believe this may be related to the XCode IDE or some kind of configuration settings...
cleaned project
deleted everything inside '~/Library/Developer/Xcode/DerivedData/ModuleCache/'
clean once more
build project
Already checked
Reference to 'X' is ambiguous
Reference to 'CMTime' is ambiguous
Reference to enum is ambiguous objective-c
Xcode: "Reference to NSString is ambiguous"
Build Settings
I have tried setting setting always_search_user_paths = No;
Always Search User Paths = Yes;
Header Search Paths[$(PROJECT_DIR)/.../.../.../SDK/Common] = recursive;
Header Search Paths[$(PROJECT_DIR)/.../.../.../SDK/Core] = recursive;
Header Search Paths[$(PROJECT_DIR)/.../.../.../GenericAppRoot] = non-recursive;
... etc
FYI app.xcodeproj exists inside ___/GenericAppRoot/Devices/iOS
File Structure
Common.h
//located in $(PROJECT_DIR)/.../.../.../SDK/Common
//Common.h
#ifndef __DTISDK_COMMON_H__
#define __DTISDK_COMMON_H__
//--------
// SDK Includes
//--------
//...
//--------------
// External Device Types
//--------------
typedef enum _E_EXTERNAL_DEVICE_TYPE
{
E_EXTERNAL_DEVICE_TYPE__PRINTER = 1,
E_EXTERNAL_DEVICE_TYPE__CARD_ENTRY_DEVICE = 2,
E_EXTERNAL_DEVICE_TYPE__PIN_ENTRY_DEVICE = 3,
} E_EXTERNAL_DEVICE_TYPE;
//...
typedef enum _E_EXTERNAL_DEVICE_CONNECTION_TYPE
{
E_EXTERNAL_DEVICE_CONNECT_TYPE__AUDIO = 1,
E_EXTERNAL_DEVICE_CONNECT_TYPE__BLUETOOTH = 2,
} E_EXTERNAL_DEVICE_CONNECTION_TYPE;
//...
#endif //__DTISDK_COMMON_H__
prefix.h
#ifndef prefix_h
#define prefix_h
#include "File.h"
#include "File.c"
#include "System.h"
#include "System.c"
#include "Common.h"
... etc
ViewController.h
#import <UIKit/UIKit.h>
#import "prefix.h"
#interface ViewController: UIViewController
#end
ViewController.m
#import "ViewController.h"
#interface ViewController()
#end
#implementation ViewController
- (void) viewDidLoad {
[super viewDidLoad];
// initialization callback from one of the SDK classes
SDKInitialize();
}
#end
Any additional help or insights would be much appreciated
You are including *.c files from prefix.h which is wrong. Always include only header files (*.h).
When you are including the implementation (*.c) files, the contents of the file is inserted as is, therefore you get the same definition in multiple places, leading to name collisions.
NSArray rather dislikes being passed a nil object as part of its constructor:
UIView *aView;
UIView *aSecondView = [[UIView alloc] init];
NSArray *array = #[aView, aSecondView];
will throw an exception at runtime when array is created.
Does clang have any facilities to try and detect this kind of error? For some trivial cases (like the one above: a stack-local variable that's never assigned to), it seems like the sort of problem the static analyzer would hit out of the park.
TL;DR: -Wuninitialized catches that specific example, __attribute__((nonnull)) for parameters to functions/methods would help catch nil as argument in general.
For this specific example (variable not specifically initialized), you can use -Wuninitialized to catch those uses:
○ xcrun clang a.m -F/System/Library/Frameworks -c -o a.o -Wuninitialized
a.m:6:22: warning: variable 'aView' is uninitialized when used here [-Wuninitialized]
NSArray *array = #[aView, aSecondView];
^~~~~
a.m:4:16: note: initialize the variable 'aView' to silence this warning
NSView *aView;// = nil;
^
= nil
1 warning generated.
For passing nullptr/NULL/nil to functions/methods, __attribute__((nonnull)) should work for most cases. Since this is an API provided by Apple, you would need to file a radar and hope they add it.
P.S: Actually, __attribute__((nonnull)) wouldn't “simply work”, in this case (if you initialized aView to nil, for example. The #[...] sequence seems to be creating an array, and calling +[NSArray arrayWithObjects: count:], where you would be able to mark the first argument as nonnull, but not the things pointed to by it. In any case, it should be relatively simple to write a clang pass for the analysis you mention. You should file a radar, since such a pass could avoid lots of time losses.
P.P.S: It seems __attribute__((nonnull)) doesn't propagate much information, which is sad. :-( If you have a function f(type arg __attribute__((nonnull))) and you pass it a variable which you initialized to nil and never touched again, it doesn't warn.
I believe you are right, Clang static analyzer hit this error out of the park. It is built into Xcode (6 Beta as I tested on). Go Building Settings > Static Analyzer - Analysis Policy > Set Analyze During 'Build' to Yes. To manually perform, go Product > Analyze.
Static analyzer can also be used as stand alone tool in command line. Other option to analyze at compile time is to add Run Script in Build Phases before Compile Sources.
bool kDebuggingEnabled = NO;
...
for(i=0; i<length; i++){
...
if (kDebuggingEnabled) {
NSLog (#"Value of variable # %i",$resultingOptions);
}
}
whenever my app is live, my code checks the condition every time regarding NSLog.
Is there any better way to improve performance of my code?
You could use preprocessor macros to turn logging on and off. A good example is the DLog macro from Marcus Zarra on the Cocoa is My Girlfriend blog.
#ifdef DEBUG
#define DLog(...) NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
#else
#define DLog(...) do { } while (0)
#endif
You would place the above in your prefix.pch file and then simply replace NSLog statements with DLog statements. You also need to make sure that DEBUG is set in your debug build configuration.
Using preprocessor macros like this means that the logging code does not get compiled into your release build so there is no performance hit for the log statements.
The blog post also contains some other useful macros for handling assertions.
You have 3 choices:
1) if you want to enable/disable your logs at build time. The preprocessor solutions are the best:
// define MY_ENABLE_LOGS in your build settings for the debug configuration only, for example
#ifdef MY_ENABLE_LOGS
#define MYLog(...) NSLog(__VA_ARGS__)
#else
#define MyLog(...) do { } while(0)
#endif
2) if you want to be able to enable/disable logs at runtime (e.g. based on some hidden preferences), your solution is likely the best. You can try to hint the compiler to optimize things a bit, though:
// tell the compiler it's unlikely that kDebuggingEnabled will be true
#define MYLog(...) do { if (__builtin_expect(kDebuggingEnabled, 0)) { NSLog(__VA_ARGS__); } } while(0)
3) last option is a little bit more complicated but can provide richer info than just logs and only depends on the kind of logging you expect to provide. The idea is to use custom probes with dtrace (which can also be used within Instruments). This only works in OS X (not in iOS). See http://www.macresearch.org/tuning-cocoa-applications-using-dtrace-custom-static-probes-and-instruments for example.
Note that you can mix 1) and 2) depending on your needs. 3) is meant to be almost zero cost when probes are not traced and can provide much richer info than a simple log string.
One caveat with these solutions: the arguments of the logs won't be evaluated when MY_ENABLE_LOGS is not defined which might change your application behavior.
Use a preprocessor macro to check if you're building for debug:
#if DEBUG
// do stuff
#end
If the preprocessor (the thing that runs before the compiler) evaluates DEBUG to be true it'll keep the code there for the compiler to compile, but if DEBUG doesn't exist or is false, it'll erase that piece of code.
I want to do an Application test that parses some json, stores to core data, and reads out some objects.
How can my code know if it's being run as part of a test or normal run? Just some way to know "are we in test target"? Because the app when it fires up now kicks off a bunch of requests to populate my coredata with info from the server. I don't want it to do this during my tests. I want to fire up the App, read HARDCODED json from a file and store this using the same methods as otherwise into coredata, and verify the results.
If someone could explain how to pass specific key-value pairs on a per target basis that can be read from within the app, I would be even more delighted.
Never mind... figured out that it is in "Schemes" that you set this.
For example if you want TARGET=TEST to be available during Test and TARGET=RUN to show during run, just set that in your Scheme > Environment Variables > Name/Value.
Then from your app you can do:
[[[NSProcessInfo processInfo] environment] objectForKey:#"TARGET"]
Using build settings with preprocessor macros DID NOT work for me b/c my test target (for application/integration testing) is dependent on my main (not test) target, so the main target is built first and that's what runs, and you end up with main target preprocessor macros even though you are after the ones defined in the target you ran. If I missed something here someone feel free to explain please.
If by "test target" you mean your unit tests (i.e. Product > Test or ⌘U), you can add a preprocessor macro to the target and check for that macro in your code. This allows something like the following:
#ifdef TEST
// Load the hard-coded data.
#else
// Load data from the server.
#endif
To do this, click on your project file in the project navigator, select your test target, click the Build Settings tab, search for "macros", double click the Preprocessor Macros option, and add one!
There two situations to deal with:
Run some code if a certain target such as Tests is selected, and
Conditionally #import some files for a certain target such as Tests.
Target Code for Test Target:
Create a macro in your ProjectName-Prefix.pch file as following:
#define IsTestTarget [[[[NSProcessInfo processInfo] environment][#"XCInjectBundle"] pathExtension] isEqualToString:#"xctest"]
and then call it anywhere in the app:
if (IsTestTarget) {
//Do something specific for test target;
} else {
//Otherwise do something else
}
Conditional #import:
To #import certain files when Tests target is selected, you do need to add a Preprocessor Macro to your Test target and use it as:
#ifdef APPTESTS
#import "TestSpecificFile.h"
#else
#import "SomeOtherFile.h"
#endif
Here is how you can add a Preprocessor Macro:
You can use the below function.
+(BOOL) isRunningTests
{
NSDictionary* environment = [[NSProcessInfo processInfo] environment];
NSString* injectBundle = environment[#"XCInjectBundle"];
return [[injectBundle pathExtension] isEqualToString:#"xctest"];
}
Usually in Unit Test programmers are using mocking classes and functionalities.
You can create a class with target membership only for the test target.
#interface MockClass : NSObject
#end
Then in the application code you can check if class exist using NSClassFromString function (which will return Nil for target not included in the class's target membership, in this case - non test target.
if (NSClassFromString(#"MockClass")) {
//Test Target
} else {
//App Target
}
And you can of curse function it
BOOL isUnitTest(){
return NSClassFromString(#"MockClass") != Nil;
}
I need to get it as a string to use elsewhere in the program, I'm not worried about compiler settings.
I found HowToGetHardwareAndNetworkInfo on CocoaDev, but it seemed a little intense when all I wanted to know is PPC vs. Intel.
If your application is built fat (i.e. isn't running under rosetta on intel), you don't need to make any calls to get this information, because different code will be running, depending on which architecture you're on. Thus, the information is available at compile time:
#if defined __i386__ || defined __x86_64__
NSString *processorType = #"Intel";
#elif defined __ppc__ || defined __ppc64__
NSString *processorType = #"PPC";
#elif defined __arm__
NSString *processorType = #"ARM";
#else
NSString *processorType = #"Unknown Architecture";
#endif
If you really want to do the determination at runtime for some perverse reason, you should be able to use the sysctlbyname function, defined in <sys/sysctl.h>.
How about uname?
struct utsname uts;
uname(&uts);
printf("%s\n", uts.machine);
Will print like PPC or i386 or x86_64 depending on the machine.
The only part of that mess which you actually care about is here:
host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount);
See the Mach headers in Kernel.framework for struct and constant definitions.