在低版本iOS系统中调用高版本API

调用高版本API会有什么问题?

在iOS7上,调用iOS8新加入的方法会导致崩溃。

- (BOOL) findString:(NSString *)s inString:(NSString *)destString {
    NSString * lowDest = destString.lowercaseString;
    return [lowDest containsString:s];
}

NSStringcontainsString方法是在iOS8才被加入的,只要在iOS7上调用了containsString方法就会直接崩溃:

在iOS7上初始化iOS8才有的对象会返回nil。虽然不会崩溃,但是可能会导致预料外的行为。

LAContext* context = [[LAContext alloc] init];
self.touchIDEnabled =
    [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                         error:&error];

LAContext是一个iOS8才加入的对象。在iOS7上,[[LAContext alloc] init]会返回nil。
调用contextcanEvaluatePolicy:error:方法,依然返回nil。self.touchIDEnabled就被设置成了NO。
按照代码逻辑,在iOS7上self.touchIDEnabled恰好就应该被设置为NO,在这个例子中不会有更严重的问题发生,但如果这个变量是self.touchIDDisabled,事情就不好说了。

一定要用低版本系统不支持的API的时候该怎么办?

iOS系统更新会带来新的接口和特性,我们总是会遇到不得不用新版本API的情况,所以代码中必须兼容新旧两种系统:

  • 通过判断iOS系统版本来兼容
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
    // Do something
}
  • 在调用API前检查respondsToSelector
if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
    [cell setPreservesSuperviewLayoutMargins:NO];
}

  • 在创建对象时判断[ClassName class]是否为nil
- (BOOL)alertControllerAvailable {
    return [UIAlertController class] != nil; // iOS 8 and later.
}

从代码的可维护性来考虑,比较推荐后两种方式。

检查API可用性的工具

比较好的方案是用MJGAvailability.h在编译期检查。
把MJGAvailability.h文件加入工程中,在预编译头文件最开头加上下面的代码即可:

#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED __IPHONE_7_0
#import "MJGAvailability.h"

其中的__IPHONE_7_0定义在Availability.h文件中,可以改成需要的任何系统版本。
配置完成后,调用不可用的API会出现如下形式的警告:

xxxxxxx.m:64:18: 'containsString:' is deprecated: TOO NEW!

如果配置后编译没有生效,把Build Settings里面的Enable Modules (C and Objective-C)项改为NO试试,具体原因我还不知道是为什么。

这个工具无法检查出我们的代码有没有进行过版本兼容处理,它会对所有有问题的代码报错。所以我们要在处理过兼容性的地方,显式的用宏把代码包起来:

MJG_START_IGNORE_TOO_NEW
    if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
        [cell setPreservesSuperviewLayoutMargins:NO];
    }
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:UIEdgeInsetsZero];
    }
MJG_END_IGNORE_TOO_NEW

另外有一个叫做Faux Pas的代码静态分析工具,可以进行API可用性的检查,但因为它的试用版随机隐藏了检查结果,所以就没再研究了。

你可能感兴趣的:(在低版本iOS系统中调用高版本API)