前言
之前写过这样一篇文章:
文中提到了在iOS10中,关于系统API里CAAnimationDelegate协议的一些变化以及一种较为妥协的的适配写法。此文是基于上文的一个补充和完善,并提出一些相对更为完善的写法。
为了方便和节省大家的时间,先在此直接给出下文中提到的一种最简单的写法,对于探究过程没有兴趣的可以直接忽略下文(但是还是强烈建议看看,一方面这么写一项都不能缺省是有重要原因的,另一方面如果你还有其他想法,欢迎据此给出指正或建议)
#if defined(__IPHONE_10_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0)
@interface ViewController () <CALayerDelegate>
#else
@interface ViewController ()
#endif
@end
1.起因
写这篇文章的起因是前阵子在写一个demo时,用到了CALayerDelegate
的相关协议方法,并遇到了和CAAnimationDelegate
一样的情况,警告。
查看API,也看到了类似的变化:
再对比下10以前的:
同样的变化,由基类的分类这种非正式协议,变成了正式的协议声明。再看下这个代理的前后变化:
本来也是直接按照之前的方式来解决这个警告的,但是细想下来,不知道这样的变化还有多少,只是目前遇到的,都只是QuartzCore框架下的。这或许是苹果在规范自己的API?
2.关于这里的适配
这里说的适配,并不是像适配某一个方法是否兼容某一系统版本那样的适配,因为那种版本适配不做的话,影响的是APP的兼容性。而这里的适配,并不会影响APP的兼容性,有试过直接用低版本真机编译这些未经适配的协议代码,执行并不会有什么影响。具体机理我并不能说太清楚,但是毕竟这只是一种针对原有API的修改,而不是新增。(其实我一直是很好奇像NS_AVAILABLE_IOS
这类相关的宏标示的方法和其指定的低版本系统不兼容的原因,是系统执行这些代码时会遇到什么问题吗?求解。)
那么做这样的适配意义又何在?
开发兼容。
一个团队协作开发,可能因为各种原因,每个人的xcode版本并不能保持一致(适配iOS7及以下的机器用xcode8调试不了、低版本系统的mac装不了新的xcode……)。
还有就是我们在写一些开源三方时,不做这样的兼容适配,有些使用低版本xcode的开发者,在使用你的代码时,可能会因此报错,从而造成一些不必要的麻烦。
3.更好的适配写法
这里的更好,是相对之前那篇文章来的。
之前那样写,是因为不能确定开发者xcode版本,从而不能直接使用__IPHONE_10_0
这类宏。
3.1.尝试的方案一
#if (__IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_10_0) || (__IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_10_1)
@interface ViewController () <CALayerDelegate>
#else
@interface ViewController ()
#endif
@end
既然不能确定__IPHONE_10_0
宏是否存在,从而不能直接写__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
这样的判定语句(因为若__IPHONE_10_0
宏不存在,这个逻辑恒成立从而无效),那么改为判等应该就没问题了,可惜因为10.1也出来了,只能增加或逻辑。不过,这样做,同样带来了维护的代价,系统肯定是要持续迭代的,每次迭代都要新增或条件,这样做麻烦不说,风险也是远大于之前的做法的。。。
3.2.改进的方案二
#ifdef __IPHONE_10_0
@interface ViewController () <CALayerDelegate>
#else
@interface ViewController ()
#endif
@end
既然是因为无法判断__IPHONE_10_0
宏是否存在,那么就以此为条件岂不是正好?而且这类宏是随版本迭代持续递增的,那么只要__IPHONE_10_0
存在,应该就可以确认xcode版本至少是8,毕竟__IPHONE_10_1
肯定是之后出现的。
然而,经过实际实验……在xcode7.3.1上依旧报错:CALayerDelegate
协议不存在,#ifdef
条件分支依旧参与编译了……why???
首先这样的逻辑应该是没问题的,那就查看xcode7.3.1的API吧。果然,在CABase.h
文件中发现了这样的一段定义:
什么鬼???9.3.1还要涉及10.0了?难怪之前的条件可以成立……
再次翻看xcode8.1中关于CABase.h
中的相关部分,好嘛,这些已经全都没有了,整个头文件的宏定义改了一大半,基本“面目全非”了,果然是善变啊……
3.3.完善的方案三
有了之前两次尝试,方案三应运而生,也就是文章开头的那个了:
#if defined(__IPHONE_10_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0)
@interface ViewController () <CALayerDelegate>
#else
@interface ViewController ()
#endif
@end
这就是为什么开头有提到,一项都不能缺省,否则后果见方案二。
这里除了判断__IPHONE_10_0
宏是否定义,还判断了当前系统的版本是否是大于10.0的,其实主要是为了判断xcode的版本是否是大于8.0的(第2部分提到的开发适配)。
#if defined(__IPHONE_10_0)
这个等效于#if defined __IPHONE_10_0
,也就是我们通常写的#ifdef __IPHONE_10_0
的简化后的单条件版本,用#if defined
更为灵活,可以判断多个条件。
当然这里也可以根据习惯拆开写,即先写#ifdef __IPHONE_10_0
,再写#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
,只是多了几步,意思相同。
至此,针对这部分的适配结束了。最终的方案应该是较为完善的了,目前也没有想到更合适的了。实际使用测试,还没有发现什么问题,如果你遇到了,欢迎及时反馈。
参考文章:
1.C语言的条件编译#if, #elif, #else, #endif、#ifdef, #ifndef
2.#if、#ifdef、#if defined之间的区别
3.#if defined和#if !defined(c语言的宏定义)