关键词: iOS 低版本 调用 高版本 API
我们在编程过程中经常遇到新的Api无法兼容低版本系统造成Crash问题,如何让Xcode编译时对于高于工程版本的Api显示警告呢?今天特意研究一番:
一般是通过两种方式:
方案一:
使用开源的MJGAvailability.h
把MJGAvailability.h文件加入工程中,在预编译头文件最开头加上下面的代码即可:
#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED __IPHONE_7_0
#import "MJGAvailability.h"
一般需要配置Enable Modules
为NO;大多数工程可能不适合,所以效果一般;
方案二:
修改Xcode中的iOS SDK
在Xcode中,NS_AVAILABLE(10_5, 2_0)
我们通过其宏定义一步步点进去:
1)、NS_AVAILABLE(10_5, 2_0)
2)、#define NS_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
3)、#define CF_AVAILABLE(_mac, _ios) __attribute__((availability(macosx,introduced=_mac)))
所以,我们可以通过重写CF_AVAILABLE(_mac, _ios)
来提示是否应该API是否合法
其宏定义所属文件夹:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFAvailability.h
在该文件夹下打开CFAvailability.h
(注意不要使用Xcode来打开(需要权限),用其他编辑软件来打开,比如:Sublime等)
我这里是针对自己的iOS工程最低适配到iOS8.0,所以在(CFAvailability.h 60行)#elif (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
这个条件下重写CF_AVAILABLE_IOS(_ios)
, CF_AVAILABLE(_mac, _ios)
这两个宏定义,具体代码如下:
#define __NEP_2_0 availability(ios,introduced=2.0)
#define __NEP_2_1 availability(ios,introduced=2.1)
#define __NEP_2_2 availability(ios,introduced=2.2)
#define __NEP_3_0 availability(ios,introduced=3.0)
#define __NEP_3_1 availability(ios,introduced=3.1)
#define __NEP_3_2 availability(ios,introduced=3.2)
#define __NEP_4_0 availability(ios,introduced=4.0)
#define __NEP_4_1 availability(ios,introduced=4.1)
#define __NEP_4_2 availability(ios,introduced=4.2)
#define __NEP_4_3 availability(ios,introduced=4.3)
#define __NEP_5_0 availability(ios,introduced=5.0)
#define __NEP_5_1 availability(ios,introduced=5.1)
#define __NEP_6_0 availability(ios,introduced=6.0)
#define __NEP_6_1 availability(ios,introduced=6.1)
#define __NEP_7_0 availability(ios,introduced=7.0)
#define __NEP_7_1 availability(ios,introduced=7.1)
#define __NEP_8_0 availability(ios,introduced=8.0)
#define __NEP_8_1 availability(ios,introduced=8.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_8_2 availability(ios,introduced=8.2) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_8_3 availability(ios,introduced=8.3) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_9_0 availability(ios,introduced=9.0) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_9_1 availability(ios,introduced=9.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_9_2 availability(ios,introduced=9.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_9_3 availability(ios,introduced=9.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_9_4 availability(ios,introduced=9.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_9_5 availability(ios,introduced=9.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_0 availability(ios,introduced=10.0) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_1 availability(ios,introduced=10.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_2 availability(ios,introduced=10.2) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_3 availability(ios,introduced=10.3) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_4 availability(ios,introduced=10.4) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_5 availability(ios,introduced=10.5) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_6 availability(ios,introduced=10.6) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_7 availability(ios,introduced=10.7) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_8 availability(ios,introduced=10.8) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_9 availability(ios,introduced=10.9) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_10_10 availability(ios,introduced=10.10) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_11_1 availability(ios,introduced=11.1) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_11_2 availability(ios,introduced=11.2) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_11_3 availability(ios,introduced=11.3) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_11_4 availability(ios,introduced=11.4) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_11_5 availability(ios,introduced=11.5) deprecated("mqc:::: API version newer than iOS8.0")
#define __NEP_11_6 availability(ios,introduced=11.6) deprecated("mqc:::: API version newer than iOS8.0")
#define CF_AVAILABLE_IOS(_ios) __attribute__((,__NEP_##_ios))
#define CF_AVAILABLE(_mac, _ios) __attribute__((,__NEP_##_ios))
具体提示内容可以自定义。
注意:
以上方式针对Xcode8版本是有效的,针对Xcode9
Xcode9以后上述方式会出现Xcode编译出错,原因是:
在iOS11的frameworks中出现了NS_CLASS_AVAILABLE_IOS(8.0)
这样的宏,Xcode之前的代码一般的写法为NS_CLASS_AVAILABLE_IOS(8_0)
这样的下划线方式,而且这个地方是苹果宏定义聚集的地方,应该非常忌讳用小数点来表示,我的猜测多半是新员工搞事情;
如果是“8.0”这样的方式出现我们再使用宏定义的方式改写苹果的API肯定是不行,因为“ISO C99”不支持这样的字符串格式。
不过好在Xcode9已经支持检测低版本API的兼容问题,你只需要搜索“is only available on iOS 8.0”查看适配情况就OK了。例如:
'UIImageRenderingModeAlwaysOriginal' is only available on iOS 7.0 or newer
只需要点击fix就行。
如果Xcode9提供的你就是不想用(任性),重写#define NS_CLASS_AVAILABLE_IOS(_ios) NS_CLASS_AVAILABLE(NA, _ios)
这个宏定义;
思路:
在“CFAvailability.h”文件中新建一个宏定义的,然后通过重写“NSObjCRuntime.h”文件中的#define NS_CLASS_AVAILABLE_IOS(_ios) NS_CLASS_AVAILABLE(NA, _ios)
为新加的宏定义即可。