I need to write a lib in my iOS app.
The statement should be pre-process define as :
myObject ...
#if ARC
// do nothing
#else
[myObject release]
#endif
or run-time process as:
if (ARC) {
// do nothing
} else {
[myObject release];
}
How can I do?
Please help me! Thank you.
You can use __has_feature, like so:
#if __has_feature(objc_arc)
// ARC is On
#else
// ARC is Off
#endif
If you want to also build with GCC (Apple's GCC does not support ARC), you may also need the following to determine the compiler:
#if defined(__clang)
// It's Clang
#else
// It's GCC
#endif
Update
Combined, they would take the general form:
#if defined(__clang)
#if !defined(__has_feature)
// idk when clang introduced this
#error This version of clang does not support __has_feature
#endif
#define MON_IS_ARC_ENABLED_IN_THIS_TRANSLATION __has_feature(objc_arc)
#else
// for every compiler other than clang:
#if defined(__has_feature)
#error Another compiler supports __has_feature
#endif
#define MON_IS_ARC_ENABLED_IN_THIS_TRANSLATION 0
#endif
Then just use MON_IS_ARC_ENABLED_IN_THIS_TRANSLATION in your sources or for further #defines.
If a compiler you use adds support, you would have to add a case for that (and compiler errors would likely catch the error in this case, since it would likely forbid use of ref count ops).
Note that this has extra checks to demonstrate how one can (and should) avoid defining reserved identifiers (based on a conversation in the comments). It's not exhaustive, but a demonstration. If you find yourself writing conditional __has_feature checks often, you may want to define a new macro for that to reduce and simplify definitions.
You can do it using macros:
#if !defined(__clang__) || __clang_major__ < 3
#ifndef __bridge
#define __bridge
#endif
#ifndef __bridge_retain
#define __bridge_retain
#endif
#ifndef __bridge_retained
#define __bridge_retained
#endif
#ifndef __autoreleasing
#define __autoreleasing
#endif
#ifndef __strong
#define __strong
#endif
#ifndef __unsafe_unretained
#define __unsafe_unretained
#endif
#ifndef __weak
#define __weak
#endif
#endif
#if __has_feature(objc_arc)
#define SAFE_ARC_PROP_RETAIN strong
#define SAFE_ARC_RETAIN(x) (x)
#define SAFE_ARC_RELEASE(x)
#define SAFE_ARC_AUTORELEASE(x) (x)
#define SAFE_ARC_BLOCK_COPY(x) (x)
#define SAFE_ARC_BLOCK_RELEASE(x)
#define SAFE_ARC_SUPER_DEALLOC()
#define SAFE_ARC_AUTORELEASE_POOL_START() #autoreleasepool {
#define SAFE_ARC_AUTORELEASE_POOL_END() }
#else
#define SAFE_ARC_PROP_RETAIN retain
#define SAFE_ARC_RETAIN(x) ([(x) retain])
#define SAFE_ARC_RELEASE(x) ([(x) release])
#define SAFE_ARC_AUTORELEASE(x) ([(x) autorelease])
#define SAFE_ARC_BLOCK_COPY(x) (Block_copy(x))
#define SAFE_ARC_BLOCK_RELEASE(x) (Block_release(x))
#define SAFE_ARC_SUPER_DEALLOC() ([super dealloc])
#define SAFE_ARC_AUTORELEASE_POOL_START() NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#define SAFE_ARC_AUTORELEASE_POOL_END() [pool release];
#endif
The above came from the site: http://raptureinvenice.com/arc-support-without-branches/; but I've pasted it to ensure it's not lost.
You generally do not want to do things like this:
#if ARC
// do nothing
#else
[myObject release]
#endif
Because it’s a recipe for disaster, there are many subtle bugs lurking in such code. But if you do have a sane use case for that, you would perhaps be better off with a macro (I didn’t know __has_feature, thanks Justin!):
#if __has_feature(objc_arc)
#define MY_RELEASE(x) while (0) {}
#else
#define MY_RELEASE(x) [x release]
#endif
But I would be quite nervous to use even this, the pain potential is huge :)
Related
In my MotionManager wrapper class I have this one code snippet that repeats for each of it's function. This code is there to make the app runable on simulator without otherwise required gyroscope in device. The code snippet I'm using looks like this:
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
return // Do nothing if in simulator
#endif
I would like to replace this code with a single line of code stating clearly what it does and isn't so ugly. If I was writing this in C++ I would simply use macro that would look something like this:
#define skipIfSimulator #if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE /
return /
#endif
So instead of this:
func foo() {
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
return // Do nothing if in simulator
#endif
...
}
There would be this:
func foo() {
skipIfSimulator
...
}
What is the best way to achieve this while adding preferably no overhead?
I've come up with a pretty poor work-around. But it can satisfy your need.
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
BOOL const kIsSimulator = NO;
#else
BOOL const kIsSimulator = YES;
#endif
#define SIM(x) if (kIsSimulator) {x; return;}
Usage:
SIM(`any_statement`);
Example usage:
SIM(NSLog(#"on Simulator"));
or,
SIM(); // No statement at all
Self contained example: Objective-C
#import "ViewController.h"
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
BOOL const kIsSimulator = NO;
#else
BOOL const kIsSimulator = YES;
#endif
#define SIM(x) if (kIsSimulator) {x; return;}
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[self printAMessage];
}
- (void)printAMessage {
SIM(NSLog(#"on Simulator"));
NSLog(#"on iPhone");
}
#end
Unfortunately can't reproduce same with Swift.
Can I do something as simple as:
myHeaderFile~iphone.h
myHeaderFile~iPad.h
and then: #import "myHeaderFile.h"
?
I'm assuming no, but you get the idea. Any tips?
If I try using macros it doesn't work, because by the time the macros are parsed, the app is already running. I just need that for loading different definitions for different screen resolutions.
How about something like this instead?
#ifdef UI_USER_INTERFACE_IDIOM()
#define IS_IPAD() (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#else
#define IS_IPAD() (false)
#endif
Then you can selectively build code like:
if (IS_IPAD()){
// do something for iPad
}
else {
// do something for iPhone/iPod
}
I am using conditional code as below,
I want to run certain code only in ios5.0 and > ios5.0( i mean i want to support ios5.0 and 5.1 version too)
But the below condition dos not seem to work. ( Currently my development version is 5.1 but the below snippet is not getting identified.the control is not going into it.)
Please let me know your thoughts
#ifdef __IPHONE_5_0_OR_LATER
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_5_0
// iPhone 5.0 code here
#endif
#define __IPHONE_2_0 20000
#define __IPHONE_2_1 20100
#define __IPHONE_2_2 20200
#define __IPHONE_3_0 30000
#define __IPHONE_3_1 30100
#define __IPHONE_3_2 30200
#define __IPHONE_4_0 40000
#define __IPHONE_4_1 40100
#define __IPHONE_4_2 40200
#define __IPHONE_4_3 40300
#define __IPHONE_5_0 50000
#define __IPHONE_5_1 50100
#define __IPHONE_NA 99999 /* not available */
How to target a specific iPhone version?
#ifdef is a compile directive, thus it will be evaluated at compile time not run time.
Thus if you add this to you code, the methods call in the if will all ways be called if your target SDK matches your #ifdef. So if you compile an app for both iOS 4 and 5 and place all the 5 only methods in #ifdef io5 the app will crash on iOS 4 since the methods will be called.
If you want to check if some method is available then you should do like :
Here is an example for dismissing an modal view controller from it's parent. Since parentViewController is changed to presentingViewController in iOS 5, we check if presentingViewController is available and use it.
if ([self respondsToSelector:#selector(presentingViewController)]) {
[self.presentingViewController dismissModalViewControllerAnimated:YES];
} else {
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
The same with goes for checking if a class is available :
if ([MPNowPlayingInfoCenter class]) {
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *songInfo = /* ... snip ... */;
center.nowPlayingInfo = songInfo;
}
NSArray *versionCompatibility = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
if ( 5 == [[versionCompatibility objectAtIndex:0] intValue] ) { /// iOS5 is installed
// Put iOS-5 code here
} else { /// iOS4 is installed
// Put iOS-4 code here
}
#ifdef doesn't work. But why?
CGFloat maxScale;
if ( [[UIScreen mainScreen] respondsToSelector: #selector (scale)] == YES )
{
NSLog (#"case1");
#define GLOBAL1
}
else
{
NSLog (#"case2");
#undef GLOBAL1
}
#ifdef GLOBAL1
NSLog (#"first");
maxScale = 1.0 / [[UIScreen mainScreen] scale];
#else
NSLog (#"second");
maxScale = 1.0;
#endif
#undef GLOBAL1
my log:case1, second. But it must be case1, first.
#define, #ifdef are pre-processor macros/conditionals. That means that the logic contained in them is compiled before your code is compiled. It is not actually part of your code.
See this guide for learning what pre-processor macros/conditionals are and do.
[EDIT]
This is what your pre-processor sees when it reads your code.
#define GLOBAL1
#undef GLOBAL1
#ifdef GLOBAL1
//...
#else
//...
#endif
#undef GLOBAL1
it IGNORES all other code and logic.
This is the actual code output the compiler makes:
if ( [[UIScreen mainScreen] respondsToSelector: #selector (scale)] == YES )
{
NSLog (#"case1");
}
else
{
NSLog (#"case2");
}
// because the pre-processor #undef GLOBAL1
NSLog (#"second");
maxScale = 1.0;
The pre-processor code is "executed" telling the compiler how to compile, and will not be used or run during run-time.
Hope that helps!
The preprocessor does not care that the #define is inside a coded if statement - it is processed before the code and only cares about other preprocessor definitions. You can't use #defines and other preprocessor commands (such as #undef) as code- they will not be hit each time the code enters the conditional branches.
Using the TARGET_IPHONE_SIMULATOR macro results in the same constant values being defined in am application. For example:
#ifdef TARGET_IPHONE_SIMULATOR
NSString * const Mode = #"Simulator";
#else
NSString * const Mode = #"Device";
#endif
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
...
NSLog(#"Mode: %#", Mode);
...
}
Always results in "Mode: Simulator" being logged. I'm currently running XCode 3.2.4 if that helps. Thanks.
TARGET_OS_SIMULATOR is defined on the device (but defined to false). The fix is:
#include <TargetConditionals.h> // required in Xcode 8+
#if TARGET_OS_SIMULATOR
NSString * const Mode = #"Simulator";
#else
NSString * const Mode = #"Device";
#endif
Not sure when this was changed. I'm fairly sure it was possible to use 'ifdef' in the past.
For me explicitly including TargetConditionals.h helped
#include <TargetConditionals.h>
Try TARGET_OS_SIMULATOR, as TARGET_IPHONE_SIMULATOR is deprecated.
I would try implement macro if its going to be used on different classes through out the app.
in pch file ,
#if TARGET_IPHONE_SIMULATOR
#define isSimulator() YES
#else
#define isSimulator() NO
#endif
and in any class I can check by calling isSimulator().
For some reason TARGET_IPHONE_SIMULATOR doesn't work for me in xcode v6.4 . The snippet below works perfectly :
#if (!arch(i386) && !arch(x86_64))
camera = Camera()
#else
camera = MockCamera()
#endif
Swift:
#if targetEnvironment(simulator)
showSimulatorOnlyError()
#endif