Deployment Target VS Base SDK
在做向下兼容时,有两个术语不得不了解:Deployment Target和Base SDK。
Base SDK一般就是最新的iOS版本,可通过:Build Settings -> Architecture查看。
Deployment Target是App可支持的最旧的iOS版本。
不支持的Frameworks
连接Deployment Target不存在的framework会导至动态连接器崩溃你的App。所以把不受支持的framework设置为“Optional”, 也被称为软连接(weak linking):
动态检查类是否可用 Unsupported classes
- weak linking
if ([SomeNewVersionClass class] != nil) {
// that framework is available
} else {
//Fail gracefully
}
如果没有进行软连接此方法会导至崩溃
- querying the runtime
if (NSClassFromString(@"SomeNewVersionClass") != nil) {
//available
} else {
//Fail gracefully
}
Unsupported methods
//instance method:
if ([instance respondsToSelector:@selector(someNewMethod)]) {
// Safe to use.
} else {
//Fail gracefully
}
//class method:
if ([someClass respondsToSelector:@selector(someNewClassMethod)]) {
//safe to use
} else {
//Fail gracefully
}
Unsupported constants/C functions
有时deployment target会缺失一些常量,比如:extern NSString *和C函数。此情况下,我们可用NULL判定它是否存在
举个例子:
C函数ABAddressBookCreateWithOptions(...)iOS6才引进的,在iOS5版本的App可以这么做:
if (ABAddressBookCreateWithOptions != NULL) {
//Safe to use
} else {
//Fail gracefully
}
The same approach applies to constants. For example, iOS 4.0 introduced multitasking support. If you wanted to check for the existence of UIApplicationWillEnterForegroundNotification, you would simply validate it as so:
if (&UIApplicationWillEnterForegroundNotification) {
//Safe to assume multitasking support
}
else {
//Fail gracefully
}
Unsupported enumeration values
Checking for the existence of enumeration or bit mask values — the kind that you would find inside an NS_ENUM or NS_OPTIONS declaration — is incredibly difficult to check at runtime. Why?
Under the hood, an enumeration is just a method of giving names to numeric constants. An enum is replaced with an int when compiled, which always exists!
If you’re faced with the situation of needing to see if a certain enumeration exists, all you can do is either explicitly check the OS version (which isn’t recommended) or alternatively check against another API element that was introduced at the same time as the new enumeration value.
Note: Whichever way you do this, be sure to add adequate comments to any such code and consider wrapping the code in a dedicated compatibility helper.
Explicit iOS version checking
- 方法一
/*
* System Versioning Preprocessor Macros
*/
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
/*
* Usage
*/
if (SYSTEM_VERSION_LESS_THAN(@"4.0")) {
}
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"3.1.1")) {
}
上述方法不推荐使用,请使用苹果官方推荐的方法。
- 苹果推荐方法
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1)
{
// Load resources for iOS 6.1 or earlier
}
else
{
// Load resources for iOS 7 or later
}
条件编译
当你使用了最新的iOS版本才引进的框架,但是你的同事可能使XCode版本过低无法识别这个新框架导至编译不通过。
而你又没办法要求所有同事都更新到最新的XCode,此时你可以用条件编译,使代版本的XCode也可以编译通过。
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
#import <CallKit/CallKit.h>
#endif
//...
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
if (NSClassFromString(@"CXCallObserver") != nil) {
CXCallObserver *callObserver = [CXCallObserver new];
[callObserver setDelegate:self queue:nil];
self.callObserver = callObserver;
}
#endif
// ...
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
if (!call.hasConnected && !call.hasEnded && !call.isOnHold && !call.isOutgoing ) {
// 监听电话打入事件
}
}
#endif
https://www.raywenderlich.com/42591/supporting-multiple-ios-versions-and-devices
http://stackoverflow.com/questions/14002304/how-to-import-social-framework-only-for-ios-6
http://stackoverflow.com/questions/3339722/how-to-check-ios-version
http://tutuge.me/2015/07/25/compatibility-with-macro-category-and-runtime/
http://roadfiresoftware.com/2014/01/the-right-way-to-check-the-ios-version-in-an-app/