如你所知,已废弃(Deprecated)的API指的是那些已经过时的并且在将来某个时间最终会被移除掉的方法或类。通常,苹果在引入一个更优秀的API后就会把原来的API给废弃掉。因为,新引入的API通常意味着可以更好的发挥新硬件或操作系统的性能,或者可以使用一些在构建原有API时根本还没有的语言特性(e.g. blocks)。

每当苹果添加新方法的时候,他们都会在方法声明的后面用一个很特殊的宏来标明哪些iOS版本支持它们。例如,在UIViewController中,苹果引入了一个使用block来处理回调的方法用来展示一个模态controller,它的声明是这样的:

1
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion NS_AVAILABLE_IOS(5_0);

注意到NS_AVAILABLE_IOS(5_0)了吗?这就告诉我们这个方法可以在iOS5.0及以后的版本中使用。如果我们在比指定版本更老的版本中调用这个方法,就会引起崩溃。

那被这个方法替换了的那个旧方法又怎么样了呢?同样,它的声明后面也带了一个类似的语法,表示它已经被废弃了:

1
- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated NS_DEPRECATED_IOS(2_0, 6_0);

NS_DEPRECATED_IOS(2_0, 6_0)这个宏中有两个版本号。前面一个表明了这个方法被引入时的iOS版本,后面一个表明它被废弃时的iOS版本。被废弃并不是指这个方法就不存在了,只是意味着我们应当开始考虑将相关代码迁移到新的API上去了。

还有类似形式的一些宏用在iOS和OS X共用的类上。比如NSArray中的这个方法:

1
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx NS_AVAILABLE(10_8, 6_0);

这里的NS_AVAILABLE宏告诉我们这方法分别随Mac OS 10.8和iOS 6.0被引入。和NS_DEPRECATED_IOS类似,也有个宏叫NS_DEPRECATED,但它的参数要稍微复杂些:

1
- (void)removeObjectsFromIndices:(NSUInteger *)indices numIndices:(NSUInteger)cnt NS_DEPRECATED(10_0, 10_6, 2_0, 4_0);

这里表示这个方法随Mac OS 10.0和iOS 2.0被引入,在Mac OS 10.6和iOS 4.0后被废弃。

Easy Come, Easy Go

上周我们讨论了在iOS7和Mac OS 10.9 SDK中被新引入的Base64 API。有趣的是,有一组有相同功能的Base64方法,在被引入的同时也被废弃掉了。为什么苹果在引入一个API的同时又把它废弃掉了?那不是毫无意义的吗?好吧,其实也不是——它在下面这种情况下就非常有意义:

实际上,这些现在已经废弃的Base64方法从iOS4和Mac 0S 10.6开始就一直存在,只是它们是私有的。直到现在苹果才把它们公开,大概是苹果一直对它们的实现不满意,一直都想把它们改写。

果然,在iOS7中,苹果选定了一个他们感到满意的Base64 API,并且将它添加到了NSData的一个公有类别中。但现在,他们知道老方法已经被取代,不会被改写了,因此他们把它公开出来。当开发者的app仍然需要支持iOS6及以前的版本时,就有了一个系统内置的Base64 api可以用。

这就是为什么,如果你查看这些新API的方法声明,可以看到NS_DEPRECATED宏部分中的起始版本是4_0,虽然实际上直到iOS7之前,它从来都没有被作为公有API被引入过:

1
- (NSString *)base64Encoding NS_DEPRECATED(10_6, 10_9, 4_0, 7_0);

这告诉你,基于iOS7 SDK开发的app如果调用了这个方法,它同样可以运行在iOS4+或Mac OS 10.6+的系统上而不会崩溃。很有用的吧?

如何使用已废弃的API

那么,如果我们有一个app需要同时支持iOS6和iOS7,想用内置的Base64方法,我们该怎么做呢?事实上,这相当简单,你只需要调用这些废弃的API就可以了。

那样编译器不是会产生警告吗?不会——只有你的deployment target版本号设置成大于或等于方法被弃用的版本号的时候才会收到编译器警告。只要你仍然在支持那些还没有废弃这个方法的iOS版本,都不会收到警告。

那么,如果苹果决定在iOS8中移除已弃用的Base64方法,你的应用程序会发生情况?简单来说,它肯定会崩溃,但是不要让这把你吓跑了:苹果不可能只在几个iOS版本后就将已废弃的API给移除(绝大多数已废弃的API在任何的iOS版本中都还没有被移除),除非你决定不再更新你的app,否则在你放弃支持iOS6之前有很多机会都可以更新到新的API。

但是如果假定我们在最坏的情况下(例如:我们不更新我们的app了,而苹果突然宣布了一个零容忍的不再向下兼容的政策),怎样让我们的代码保持永不过时并且仍然能够支持旧的系统版本呢?

这其实很简单,我们只需要做一些运行时的方法检测。使用NSObject的respondsToSelector:方法,我们可以检测,如果新的API存在,我们就调用它。否则,我们退回到已废弃的API。很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSData *someData = ...
NSString *base64String = nil; // Check if new API is available
if ([someData respondsToSelector:@selector(base64EncodedDataWithOptions:)])
{
// It exists, so let's call it
base64String = [someData base64EncodedDataWithOptions:0];
}
else
{
// Use the old API
base64String = [someData base64Encoding];
}

此代码在iOS4及以上版本中有效,并且如果苹果在未来的iOS版本中移除base64Encoding方法后,同样可以正常工作。

为其他开发者编码的时候

如果你是在写一个app,这一切都很好,但是如果你是在编写一个给其他人使用的代码库呢?如果project的target是iOS4或iOS6的时候,上面的代码会工作的很好。但是如果deployment target是iOS 7+的时候,你就会收到编译器警告,说你使用了已废弃的base64Encoding方法。

该代码实际上永远都可以正常工作,因为那个方法在运行时永远都不会被调用(因为respondsToSelector:那个检查在iOS7上总是会返回YES)。但是可惜的是,编译器还不是足够的聪明能发现这点。而且,比如像我,你不会想用那些会产生编译器警告的第三方库,你肯定也不想自己的库中产生任何警告。

那么,我们如何改写我们的代码,以便它可以用于任何deployment target,而不会产生警告?幸好,有一个编译器宏指令可以基于不同的deployment target做不同的代码分支。取决于app是为哪个最小的iOS版本编译的,我们可以用__IPHONE_OS_VERSION_MIN_REQUIRED这个宏来生成不同的代码。

下面的代码可以工作在任何的iOS版本上(不管是过去的还是将来的),而且不会产生任何警告:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0

// Check if new API is not available
if (![someData respondsToSelector:@selector(base64EncodedDataWithOptions:)])
{
// Use the old API
base64String = [someData base64Encoding];
}
else #endif {
// Use the new API
base64String = [someData base64EncodedDataWithOptions:0];
}

看清楚我们在这里做了什么吗?我们变换了respondsToSelector:的用法:我们用它来测试是否新的API不可用,然后将整段代码放到一个条件代码块中,这样它就只会在deployment target比iOS7低的情况下才会被编译。如果app是为iOS6编译的,它就会先检查新的API是否存在,如果不存在就调用旧的API。如果app是为iOS7编译的,那一整块逻辑代码都会被跳过,直接调用新的API。

关于iOS和OS X废弃的API你需要知道的一切的更多相关文章

  1. [译]关于iOS和OS X废弃的API你需要知道的一切

    原文: Everything You Need to Know about iOS and OS X Deprecated APIs 如你所知,已废弃(Deprecated)的API指的是那些已经过时 ...

  2. 关于iOS和OS X废弃的API知识点

    今天在查看苹果接口文档时,突然对于接口的声明知识点比较感兴趣,再网络找到下面这个比较不错的文章,记录一下并分享: 如你所知,已废弃(Deprecated)的API指的是那些已经过时的并且在将来某个时间 ...

  3. iOS书摘之编写高质量iOS与OS X代码的52个有效方法

    来自<Effective Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法>一书的摘要总结 一.熟悉Objective-C 了解Objective-C语言的起源 ...

  4. Objective-C 高级编程:iOS与OS X多线程和内存管理

    <Objective-C 高级编程:iOS与OS X多线程和内存管理> 基本信息 原书名: Pro Multithreading and Memory Management for iOS ...

  5. [置顶] ArcGIS Runtime SDKs 10.2 for iOS & Android& OS X发布

    我们高兴的宣布:ArcGISRuntime SDKs 10.2 for iOS & Android & OS X正式发布!在10.2版本中,你可以在iOS.Android和Mac设备上 ...

  6. Node.app让Nodejs平台在iOS和OS X系统上奔跑

    首先呢,欢迎大家去查看相同内容的链接:http://www.livyfeel.com/nodeapp/. 由于那个平台我用的markdown语法,我也懒得改动了,就这样黏贴过来了. 这是一个惊人的恐怖 ...

  7. [转]Blocking Code Injection on iOS and OS X

    Source:http://www.samdmarshall.com/blog/blocking_code_injection_on_ios_and_os_x.html Yesterday I pos ...

  8. iOS和OS X中的bundle

    bundle也可以称之为包(package). 它在iOS和OS X中实际为一个文件夹但却当成单独的文件来对待. 每一个app都有一个bundle,并且你可以通过在xxx.app图标上右击鼠标然后选择 ...

  9. iOS SSL Pinning 保护你的 API

    随着互联网的发展,网站全面 https 化已经越来越被重视,做为 App 开发人员,从一开始就让 API 都走 SSL 也是十分必要的.但是光这样就足够了吗? SSL 可以保护线上 API 数据不被篡 ...

随机推荐

  1. 几款极好的 JavaScript 文件上传插件

    文件上传功能作为网页重要的组成部分,几乎无处不在,从简单的单个文件上传到复杂的批量上传.拖放上传,需要开发者花费大量的时间和精力去处理,以期实现好用的上传功能.这篇文章向大家推荐几款很棒的 JavaS ...

  2. Linux内核分析课程总结

    Linux内核分析课程总结 By 20135203齐岳 知识梳理 (思维导图地址http://mindmap.4ye.me/mkxM0cFh/1) 从start _ kernel构造一个新的Linux ...

  3. ie6兼容问题汇总

    这几天在查找和解决网页在ie6下的兼容性问题花了我不少的时间,参考了网上的一些解决方法和自己做出来比较有效果的给大家参考一下,也方便我日后再用到: 1.IE的cache设置为Every visit t ...

  4. C——数组下标与间址运算符

    只说一句,数组下标与间址运算符*是等价的,即:a[i] = *(a+i),看代码: int main(int argc, char* argv[]) { ] = {, , , , }; int i; ...

  5. javasript_数据结构和算法_栈

    //-----------------------------------存储结构为数组-------------------------------------------- function St ...

  6. Delphi 中同类型方法的说明

    对象的方法能定义成静态(static).虚拟(virtual).动态(dynamic)或消息处理(message).请看下面 的例子: TFoo = class procedure IAmAStati ...

  7. Ganglia安装搭建

    Ganglia的安装部署 前言 1 一.Ganglia组件 1 二.安装依赖 2 三.安装expat依赖 2 四.安装confuse 3 五.安装ganglia 4 六. 服务端配置(gmetad 节 ...

  8. Bootstrap <基础三十一>插件概览

    在前面布局组件中所讨论到的组件仅仅是个开始.Bootstrap 自带 12 种 jQuery 插件,扩展了功能,可以给站点添加更多的互动.即使不是一名高级的 JavaScript 开发人员,也可以着手 ...

  9. 静态绑定网关,防止ARP攻击

    Windows XP 下 写个批处理文件:内容如下,名称自取 @echo offard -darp -s 192.168.1.1 00-14-78-ef-10-45//绑定IP地址 WIN 7 下 1 ...

  10. Qt中2D绘图问题总结(二)----------坐标系统

    坐标系统 使用QPainter绘制时使用到逻辑坐标,然后转换成绘图设备的物理坐标. 逻辑坐标到物理坐标的映射由QPainter的worldTransform()函数.QPainter的viewport ...