最新消息:iOS编程开发交流群(6906921) ,Mac.Cocoa开发交流群(7758675) 欢迎iOS/macOS开发编程爱好及学习者加入!

iOS开发之SDK兼容性指南

编程 天狐 11962浏览 3评论

介绍

Xcode包括软件开发工具包(SDKs),使您能够创建应用程序,然后运行在特定版本的iOS或OS X(包括版本不同于我们开发时的版本) 。这种技术允许您构建一个单一的二进制,利用新特性运行在系统上,并且支持他们,更优雅的降低在旧的系统上运行。一些苹果的Frameworks基于SDK自动修改他们的行为,从而提高兼容性。

注意:本文档并不解释如何开发代码,运行在iOS和OSX上,虽然Xcode使您可以通过简单地选择一个不同的SDK切换平台,iOS和Mac应用程,有基本的设计区别。有关更多信息,请参见Migrating from Cocoa

如果你想让你的应用程序针对特定版本或多个版本的iOS或OS X,请阅读这个文档。

文档结构

这个文档包含以下章节:

  • 基于SDK(SDK-Based)的开发概述    描述了怎样基于SDK开发
  • 配置基于SDK开发的项目    描述如何使用SDK设置您的项目。
  • 使用基于SDK开发   解释了如何使用弱相关的类( weakly linked)、方法和功能,如何弱链接整个框架,。如何为不同的SDK有条件地编译,以及如何找到代码中使用的废弃API。

一、基于SDK的开发概述

苹果让SKDs对于特定版本的iOS或者OS X可用。使用这些 SDKs 允许您构建针对一个系统版本的头文件和库而不只是对于你运行的操作系统。例如:你可以为OS X 10.4构建,然而运行在OS X 10.6上。

OS X SDKS被安装为Xcode(Xcode3.2或更高版本)基本安装包的一部分, Xcode release notes 列出了每个release版本支持的SKDs。当你为了iOS开发时,总是使用从 iOS 开发者中心网站下载的 SDK

基于 SDK 的开发使用这些方法︰

  • 您可以为一个版本的操作系统和向后兼容更高版本的构建目标优化。
  • 你可以构建一个目标为一系列的操作系统版本。在旧版本可以启动,可以在新版的版本中利用新的功能。这允许你交付新的软件提供与新的特性给那些已经升级到新版本系统的客户,但是还可以允许运行在没有升级的系统上。

开发可以被部署的软件,利用不同版本iOS或OS X的特性,您可以指定要构建的iOS或OS X头和库的版本(或SDK),你也可以指定软件将要运行在更老版本的iOS 或OS X系统中。描述这些概念在第二章,"基础SDK(Base SDK)和部署目标(Deployment Target)设置"

在框架中的行为选择

随着框架通过各种版本发展,API被引入或已废弃,现有API的行为可能偶尔发生变化。苹果尽一切努力尽量减少可能导致不兼容的更改。在某些情况下提供基于框架版本的替代行为,在极少数情况下,您的代码需要确定框架版本并相应地进行调整。

作为一种向后兼容性机制,苹果框架有时会检查应用程序构建的SDK的版本,如果它是较旧的SDK,请修改行为以实现兼容性。 这是在苹果预测或发现兼容性问题的情况下完成的。

注意:大多数版本相关的行为更改都列在框架发行说明( release notes)中,但它们不一定在参考文档中描述。 要了解从一个版本到另一个版本的差异,请仔细阅读发行说明。

通常,框架通过查看应用程序链接的系统框架的版本来检测应用程序的构建方式。 因此,当使用较新的SDK重新链接应用程序时,您可能会注意到不同的行为 - 其中一些可能会导致不兼容。 在这些情况下,因为您的应用程序正在重建,您应该同时解决这些问题。 因此,如果您对应用程序进行小更新,例如,解决一些错误,通常最好继续使用相同的构建环境和最初使用的库进行构建; 也就是说,针对原始的SDK。

在某些情况下,框架提供了默认设置(首选项),您可以使用它来获取旧的或新的行为,而与构建应用程序的SDK无关。 通常这些首选项仅用于调试目的; 在一些情况下,偏好可以全局地用于通过注册值来修改应用的行为。 如果利用这种机制,在你的代码的早期,使用NSUserDefaultsregisterDefaults方法)。

二、为基于SDK的开发配置项目

本章介绍了已安装的iOS和OS X SDK的配置,并解释了如何设置Xcode项目以使用特定的SDK。

SDK头文件和存根库

安装Xcode时,安装程序将创建一个/Developer/SDKs目录。 此目录包含一个或多个子目录,每个子目录提供特定版本的iOS或OS X提供的完整的头文件和存根库。OS X SDK以主版本命名,例如MacOSX10.6.sdk ,表示可用于主要版本的最新次要版本。

iOS的Xcode安装程序将SDK放置在/Developer/Platforms目录中,其中是每个平台的目录,例如iPhoneOS.platform。 每个平台目录又包含特定于该平台的Developer/SDKs目录。 iOS SDK由iOS的次要版本命名,例如iPhoneOS4.2.sdk。

为项目选择最新的SDK可让您使用与该SDK对应的操作系统更新中引入的新API。 当作为系统更新的一部分添加新功能时,系统更新本身通常不包含反映该改变的更新的头文件。 但是,SDK包含更新的头文件。

每个.sdk目录类似于它所代表的操作系统发行版的目录层次结构:它在其顶层具有usr,System和Developer目录。 OS X .sdk目录还包含一个Library目录。 这些目录中的每一个依次包含具有在安装了Xcode的操作系统的相应版本中存在的头文件和库的子目录。

iOS或OS X SDK中的库是仅用于链接的存根; 它们不包含可执行代码,只包含导出的符号。 SDK支持仅适用于本地构建目标。

基础SDK(Base SDK)和部署目标(Deployment Target)设置

要为Xcode项目使用特定的SDK,请在项目的构建设置( build settings)中进行两次选择。 这些选项决定您的项目可以使用哪些操作系统功能,如下所示:

  • 选择部署目标(Deployment Target):这标识您的软件可以运行的最低的操作系统版本。 默认情况下,Xcode将此设置为与基本SDK版本或更高版本对应的操作系统的版本。

此设置的Xcode构建变量名称为MACOSX_DEPLOYMENT_TARGET(OS X部署目标)和IPHONEOS_DEPLOYMENT_TARGET(iOS部署目标)。

        您可以无条件使用操作系统版本中的功能,直到并包括部署目标设置。

  • 选择基础SDK (base SDK):您的软件可以使用基本SDK相对应的所有操作系统的功能。 默认情况下,Xcode将此设置为Xcode支持的最新操作系统。

        此参数的Xcode构建设置( build setting)名称为SDKROOT(Base SDK)。

        您可以使用来自部署目标之后的系统版本的功能,包括您选择作为基本SDK的操作系统版本 - 但必须检查新功能的可用性,详情请阅读下文 "在iOS中使用弱链接的类" 和 "使用弱链接的方法和功能"

有关Xcode中构建设置的可能值和更多信息,请参阅Xcode项目管理指南(Xcode Project Management Guide)中的构建操作系统的多个版本, Xcode Build Setting Reference Running Applications in iOS Development Guide

构建应用程序时,部署目标将反映在应用程序的Info.plist文件中的MinimumOSVersion条目中。 对于iOS应用程序,MinimumOSVersion条目由App Store用于指示iOS发行版要求。

图2-1显示了一个时间线,说明部署目标和基本SDK之间的关系。

图2-1 SDK开发时间线

using_sdks_2x该图描述了具有OS X v10.4的部署目标和OS X v10.6的基本SDK的项目。 (图中的版本号表示该版本的所有版本,包括系统更新。)

在此示例中,软件可以通过版本10.4的最新更新自由使用OS X v10.0中的任何功能。 在确保每个这样的功能可用后,它可以有条件地使用OS X v10.5和10.6中的功能。

这些设置在编译时和运行时的影响如下。 如果您的代码使用如下符号:

  • 在基本SDK中未定义(例如,来自较新操作系统的符号),您会收到编译时错误。
  • 在基本SDK中定义,但标记为已弃用,您会收到编译时警告。
  • 在部署目标中定义,代码将正常链接和构建。 在运行时:

在运行早于部署目标的操作系统的系统上,如果使用该操作系统中不可用的符号,代码可能无法加载。

在运行等于或高于部署目标的操作系统的系统上,您的代码具有对于该操作系统中不可用的符号的空指针。 准备您的代码如在 "在iOS中使用弱链接的类" 和 "使用弱链接的方法和功能"中所述。

注意:OS X v10.6不支持使用大于3.0版的iOS模拟器SDK。 此外,在使用Simulator SDK构建时,二进制文件仅在与基本SDK相同的操作系统版本上运行,而不是在早期版本或更高版本上运行。

始终检查您是否使用废弃的API; 虽然仍然可用,但是不能保证将来可以使用废弃的API。 编译器会在代码中警告您存在已废弃的API,如"查找已废弃的API使用的实例"中所述。

当您更改基本SDK设置时,除了更改代码构建的头和存根库之外,Xcode还会适当调整其他功能的行为。 例如,符号查找(symbol lookup),代码补全( code completion),基于基础SDK的头文件打开,而不是当前运行的操作系统的头(如果两者不同)。 类似地,Xcode快速帮助关联机制确保文档查找使用与基本SDK相对应的文档集。

除了为整个项目设置基本SDK和部署目标之外,还可以为每个构建目标单独设置这些值。 目标设置覆盖项目设置。 (但是,一些试图与基本SDK设置相关联的Xcode功能(如符号定义和文档查找)可能会有所不同)。

弱链接和苹果框架

Xcode编译器使用附加到Apple框架标头中的符号的可用性宏来确定符号是弱连接还是强连接。 这些宏指示功能首次出现的操作系统版本。 例如,以下声明中的宏指示在OS X v10.6和iOS 4.0中开始提供enumerateObjectsUsingBlock方法(在NSArray类中):

当框架中的符号被定义为弱链接时,该符号不必在运行时存在以使进程继续运行。 静态链接器在引用该符号的任何代码模块中标识弱链接的符号。 动态链接器在运行时使用此相同的信息来确定进程是否可以继续运行。 如果存在于代码模块中的弱链接符号不存在于框架中,只要其不引用符号,则代码模块可以继续运行。 如果在其框架中存在弱链接的符号,代码可以正常使用它。

在废弃符号的情况下,可用性宏包含进一步的语法,用于指示不推荐使用该符号的操作系统的版本。 在所有情况下,符号的参考文档说明其可用性及其弃用信息(如果适用)。 可用性宏在/usr/include/目录中的Availability.h和AvailabilityMacros.h中定义。

Xcode编译器根据项目的基本SDK和部署目标设置来解释每个可用性宏。 编译器使用这些设置为MAC_OS_X_VERSION_MIN_REQUIRED和MAC_OS_X_VERSION_MAX_ALLOWED 宏分配适当的值。

例如,假设在Xcode中,将部署目标(最低所需版本)设置为"OS X v10",将基本SDK(最高允许版本)设置为"OS X v10.6"。 在编译期间,编译器将弱连接在OS X v10.6中引入的接口,同时强链接在早期版本的OS中定义的接口。 这将允许您的应用程序在OS X v10.5中运行,并在可用时使用较新的功能。

重要:Xcode中的部署目标设置必须设置为OS X 10.2或更高版本,以利用弱链接。 如果将此值设置为早期版本的OS X,则无法在项目中使用弱链接。 当为iOS构建时,弱连接可用于所有iOS版本。

在使用任何晚于部署目标的iOS或OS X版本中引入的任何符号之前,请检查该符号是否可用。 如果符号不可用,请提供备用代码路径。 有关详细信息,请参阅第三章 "使用基于SDK的开发"。

配置基于Makefile的项目

如果您有一个基于makefile的项目,您还可以通过在编译和链接命令中添加相应的选项来使用基于SDK的开发。 在基于makefile的项目中使用SDK需要GCC 4.0或更高版本。 要选择SDK,您可以对编译器使用-isysroot选项,对链接器使用-syslibroot选项。 这两个选项都需要指定所需的SDK目录的完整路径。 要在makefile中设置部署目标,请使用以下形式的makefile变量:ENVP = MACOSX_DEPLOYMENT_TARGET = 10.4。 要在makefile中使用此变量,请将其包含在编译和链接命令前。

设置前缀文件

Xcode支持前缀文件,每个源文件在构建时隐式的包含头文件。 许多Xcode项目模板自动生成前缀文件,包括适合于所选类型应用程序的 umbrella frameworks 。 为了效率,前缀文件被预编译和缓存,所以Xcode不需要在每次构建项目时重新编译许多行相同的代码。 您可以添加指令以导入应用程序所依赖的特定框架。

如果您使用基于SDK的开发,您必须确保您的前缀文件考虑到所选的SDK。 也就是说,不要前缀文件设置使用绝对路径umbrella header,例如/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h。 此绝对路径不工作,因为指定的头来自当前系统,而不是所选的SDK。

要包括umbrella framework headers,请将适当的#import <Framework/Framework.h>指令添加到您的前缀文件。 使用这种技术,编译器总是从适当的SDK目录中选择头文件。 例如,如果您的项目名为TestSDK,并且它有一个前缀文件TestSDK_Prefix.pch,请将以下行添加到该文件:

如果你使用Objective-C,最好使用#import指令,而不是#include(必须在过程化C程序中使用),因为#import保证相同的头文件不会多次被包含。

三、使用基于SDK的开发

本章介绍了在Xcode项目中使用的基于SDK的开发技术:

  • 使用弱链接的类(weakly linked classes),方法和函数来支持在多个版本的操作系统上运行
  • 弱连接整个框架
  • 有条件地为不同的SDK编译
  • 在代码中查找已废弃的API的使用
  • 在运行时确定操作系统版本或框架版本

有关弱链接的背景,请阅读章节二,"弱链接和苹果框架"。

在iOS中使用弱链接类

如果您的Xcode项目使用弱链接类,则必须在运行时确保这些类的可用性,然后再使用它们。 尝试使用不可用类可能会从动态链接器生成运行时绑定错误,这可能会终止相应的进程。

使用iOS 4.2或更高版本的基础SDK的Xcode项目应该使用NSObject类方法在运行时检查弱链接类的可用性。 这种简单,高效的机制利用了NS_CLASS_AVAILABLE类可用性宏,可用于iOS中的大多数框架。

重要:检查最新的iOS发行说明(Release Notes),以获取尚不支持NS_CLASS_AVAILABLE宏的框架列表。

对于支持NS_CLASS_AVAILABLE宏的iOS框架,将条件化为弱链接类的代码,如以下示例所示:

这是因为如果弱链接类不可用,向它发送消息就相当于发送消息到nil。 如果你子类化一个弱链接类并且超类不可用,那么子类也会显示不可用。

要使用这里所示的类方法,您需要做的更多,以确保一个框架支持NS_CLASS_AVAILABLE宏。 您还必须配置某些项目设置。 通过这些必要的设置,上述代码安全地测试类的可用性,即使在不存在类的iOS版本上运行时也是如此。 这些设置如下:

  • Xcode项目的基础SDK必须是iOS 4.2或更高版本。 构建设置编辑器( build settings editor )中此设置的名称为SDKROOT(Base SDK)。
  • 您的项目的部署目标必须是iOS 3.1或更高版本。 此设置的名称为MACOSX_DEPLOYMENT_TARGET(OS X部署目标)。
  • 项目的编译器必须是LLVM-GCC 4.2编译器或更高版本,或LLVM编译器(Clang)1.5或更高版本。 此设置的名称为GCC_VERSION(C / C ++编译器版本)。
  • 您必须确保项目的部署目标中不可用的任何框架都是弱连接,而不是必需的。中查看下文 "弱链接整个框架" 与Xcode Project Management Guide中的Linking Libraries and Frameworks

有关使用Xcode构建设置编辑器的信息(Xcode build settings editor),参阅 在Xcode Project Management Guide 中的 Building Products

在OS X(以及不符合刚才列出的一组条件的iOS项目)中,不能使用类方法来确定弱链接类是否可用。 相反,在类似于以下代码中使用NSClassFromString函数:

使用弱链接的方法,函数和符号

如果项目使用弱链接的方法,函数或外部符号,则必须在运行时确保其可用性,然后再使用它们。 如果尝试使用不可用项目,动态链接器可能会生成运行时绑定错误并终止相应的进程。

假设您将Xcode项目中的基本SDK设置为iOS 4.0。 这允许您的代码在该版本的操作系统中运行时使用该版本的功能。 假设您希望您的软件在iOS 3.1中运行,即使它不能使用该版本的操作系统中的较新功能。 通过将部署目标设置为早期版本的操作系统来允许此操作。

在Objective-C,instancesRespondToSelector: 方法就可以告诉你是否给定的方法选择器可用。例如,若要使用availableCaptureModesForCameraDevice: 方法,在iOS 4.0开始可用,您可以使用类似以下的代码:

清单3-1检查Objective-C方法的可用性

当您的代码在iOS 4.0或更高版本中运行时,它可以调用availableCaptureModesForCameraDevice: 确定视频捕获是否可用在设备上。 但是,当它在iOS 3.1中运行时,它必须假设只有静态图像捕获可用。

  • 如果你用不同的设置来构建这个代码,你会看到以下结果:

        如果您指定iphoneos3.1的基本SDK设置:构建将失败,因为availableCaptureModesForCameraDevice: 方法未在该系统版本中定义。

  • 如果您指定iphoneos4.0的基本SDK设置,然后将部署目标设置为:

        iphoneos4.0:该软件将仅运行在iOS 4.0或更高版本,并将无法在早期系统上启动。

        iphoneos3.1:该软件将运行在iOS 4.0和iOS 3.1,但将无法在早期的系统上启动。 当在iOS 3.1中运行时,软件将使用替代代码捕获图像。

通过将getter方法名称(与属性名称相同)传递给instancesRespondToSelector,来检查Objective-C属性的可用性。

要确定弱链接的C函数是否可用,实际使用的链接器将不可用函数的地址设置为了NULL。 检查函数的地址,因此,它的可用性 - 通过比较地址为NULL或nil。 例如,在部署目标早于OS X v10.5的项目中使用CGColorCreateGenericCMYK函数之前,请使用如下代码:

清单3-2检查C函数的可用性

注意:要检查函数的可用性,请明确地比较地址为NULL或nil。 您不能使用否定运算符(!)否定函数的地址以检查其可用性。 此外,请注意,C函数的名称与其地址同义。 也就是说,&myFunction等价于myFunction。

通过将其地址(而不是符号的名称)显式地比较为NULL或nil来检查外部(extern)常量或通知名称的可用性。

弱链接整个框架

如果您使用最近添加的框架 - 在部署目标之后可用的框架 - 您必须显式地弱链接到框架本身。

例如,假设您想要链接到Accelerate框架(首先在iOS 4.0中可用),以便在其可用的系统上使用其功能。 此外,假设您将部署目标设置为iOS 3.1.3,允许该版本的iOS用户在没有新功能的情况下使用您的应用程序。 在此示例中,您必须弱链接到Accelerate框架。

当使用部署目标中可用的框架时,您应该required该框架而不是弱链接。

有关如何弱链接框架的信息,请参阅  在 Xcode Project Management Guide中的"Linking Libraries and Frameworks"

有条件地编译不同的SDK

如果您使用一组源代码构建多个基本SDK,则可能需要针对正在使用的基本SDK进行条件化。 通过使用在Availability.h中定义的宏的预处理器指令来执行此操作。

注意:Availability.h头用于针对iOS和针对OS X v10.6及更高版本。 较早的AvailabilityMacros.h头在OS X v10.2中引入。 这些文件驻留在/usr/include目录中。

假设您想使用macosx10.4的基本SDK设置编译代码清单3-2所示的代码。 在构建过程中,必须屏蔽代码中引用OS X v10.5中引入的CGColorCreateGenericCMYK函数的部分。 这是因为对不可用的CGColorCreateGenericCMYK函数的任何引用将导致编译器错误。

要允许代码生成,请使用__MAC_OS_X_VERSION_MAX_ALLOWED宏:

  • 确保项目的目标是OS X而不是iOS
  • 隐藏基本SDK中不可用的代码

下面的代码段演示了这一点。 请注意,在#if比较子句中使用数字值1050而不是符号__MAC_10_5:如果代码在不包含符号定义的旧系统上加载,则比较仍然有效。

代码清单3-3使用预处理器指令进行条件编译

除了使用预处理器宏,上述代码还假定代码可以使用较新的基本SDK编译,但部署在运行OS X v10.4及更早版本的计算机上。 具体来说,它在尝试调用它之前检查弱连接的CGColorCreateGenericCMYK符号的存在。 这可以防止代码生成运行时错误,如果您针对较新的SDK构建代码但是将其部署在较早的系统上,则会发生运行时错误。 有关确定运行时检查以确定符号的存在的更多信息,请参阅第二章 "在iOS中使用弱链接类","使用弱链接的方法,函数和符号"

查找已废弃的API使用的实例

随着iOS和OS X的发展,它们所包含的API和技术有时会改变,以满足开发人员的需求。 作为这种演进的一部分,不太高效的接口被淘汰,以支持较新的接口。 附加到头文件中的声明的可用性宏可帮助您找到已废弃的接口。 参考文档还标记不推荐使用的接口。

注意:废弃并不意味着从框架或库立即删除接口。 它只是一种标记接口的方法,为此存在更好的替代方案。 您可以在代码中使用已废弃的API。 但是,苹果建议尽快迁移到较新的接口,因为可能会从未来版本的操作系统中删除已废弃的API。 有关任何推荐的替换接口的信息,请检查不推荐使用的API的头文件或文档。

如果编译具有OS X v10.5的部署目标的项目并使用标记为deprecated的接口,则编译器会发出相应的警告。 该警告包括已废弃的接口的名称以及在代码中的位置。 例如,如果不推荐使用HPurge函数,则会出现类似于以下内容的错误:

'HPurge' is deprecated (declared at /Users/steve/MyProject/main.c:51)

要查找代码中已废弃API的实例,请查找此类型的警告。 如果项目有很多警告,请使用Xcode中的搜索字段,根据"deprecated"关键字过滤警告列表。

确定操作系统或框架的版本

在极少数情况下,检查符号的运行时可用性不是一个完整的解决方案。 例如,如果方法的行为从一个操作系统版本更改为另一个操作系统版本,或者如果在先前可用的方法中修复了错误,则必须编写代码以将这些更改纳入考虑。

用于检查操作系统版本的技术取决于您的目标平台,如下所示:

  • 要在运行时检查iOS的版本,请使用如下代码:

  • 要在运行时检查OS X的版本,请使用Gestalt函数和系统版本选择器常量。

或者,对于许多框架,您可以在运行时检查特定框架的版本。 为此,使用全局框架版本常量 - (如果它们由框架提供)。 例如, Application Kit(在NSApplication.h中)声明了NSAppKitVersionNumber常量,您可以使用它来检测Application Kit框架的不同版本:

您可以与此常量的值进行比较,以确定您的代码正在运行的Application Kit的哪个版本。 一个典型的方法是对全局常量的值进行分层,并根据在NSApplication.h中声明的常量检查结果。 例如:

类似地,Foundation(在NSObjCRuntime.h中)声明NSFoundationVersionNumber全局常量和每个版本的特定值。

用于其他对象和组件的某些单独头文件也可以声明NSAppKitVersionNumber的版本号,在给定更新中有一些错误修复或功能可用时。

贴上我经常用的兼容方法

 

SDK Compatibility Guide 官方原文:https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-SW3

Framework Programming Guide  https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html

转载请注明:天狐博客 » iOS开发之SDK兼容性指南

微信 OR 支付宝 扫描二维码
为天狐 打赏
非常感谢你的支持,哥会继续努力!
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (3)

  1. 菜鸡 表示看不懂 因为不行 不能像 天狐一样直接看第一手资料
    jiangjunfu199202166年前 (2018-09-22)回复