iOS10 再谈CAAnimationDelegate相关协议的

作者: 霖溦 | 来源:发表于2016-12-02 00:49 被阅读1156次

前言

之前写过这样一篇文章:

iOS10 CAAnimationDelegate的简单适配

文中提到了在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,也看到了类似的变化:

iOS10以后的CALayerDelegate(截图自xcode8.1相关API)

再对比下10以前的:

iOS10之前的CALayerDelegate(截图自xcode7.3.1相关API)

同样的变化,由基类的分类这种非正式协议,变成了正式的协议声明。再看下这个代理的前后变化:

iOS10以后 iOS10之前

本来也是直接按照之前的方式来解决这个警告的,但是细想下来,不知道这样的变化还有多少,只是目前遇到的,都只是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文件中发现了这样的一段定义:

xcode7.3.1中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语言的宏定义)

·转载请声明·

相关文章

网友评论

    本文标题:iOS10 再谈CAAnimationDelegate相关协议的

    本文链接:https://www.haomeiwen.com/subject/rxsfmttx.html