我曾多次见过由于在iOS 3.x上使用了在4.x中引入但未经适当检查的新调用而出现崩溃性错误。
Xcode是否有一种方法可以警告在部署目标版本之后才可用的类、函数和过程?
这样,我就可以轻松地列出所有代码并确保它们被正确地条件化了。
我曾多次见过由于在iOS 3.x上使用了在4.x中引入但未经适当检查的新调用而出现崩溃性错误。
Xcode是否有一种方法可以警告在部署目标版本之后才可用的类、函数和过程?
这样,我就可以轻松地列出所有代码并确保它们被正确地条件化了。
我实际上发布了一些有助于测试这种情况的东西。它是我的MJGFoundation类集合的一部分,称为MJGAvailability.h。
我一直在使用的方法是像这样在我的PCH文件中应用它:
#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED __IPHONE_4_0
#import "MJGAvailability.h"
// The rest of your prefix header as normal
#import <UIKit/UIKit.h>
接着,它会警告(可能会有一个奇怪的弃用警告),关于你所设置的“软件最大版本”(#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
)对于正在使用的 API 来说过于新。此外,如果您没有定义 __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
,那么它会默认为您的部署目标。
我认为这很有用,因为我可以仔细检查我正在使用哪些 API,以确保它们不超出我设置的部署目标的范围。
至少在OS X 上,使用最近的clang/SDK,现在有一个-Wpartial-availability
选项(例如在“其他警告选项”中添加它)。然后可以定义以下宏来封装处理运行时测试是否支持该方法的代码。
#define START_IGNORE_PARTIAL _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpartial-availability\"")
#define END_IGNORE_PARTIAL _Pragma("clang diagnostic pop")
虽然我还没有在iOS上进行测试。
在查阅了AvailabilityInternal.h
之后,我意识到所有高于部署目标的可用版本都带有__AVAILABILITY_INTERNAL_WEAK_IMPORT
宏。
因此,我可以通过重新定义该宏来生成警告:
#import <Availability.h>
#undef __AVAILABILITY_INTERNAL_WEAK_IMPORT
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
__attribute__((weak_import,deprecated("API newer than Deployment Target.")))
将此代码放入项目的预编译头文件中,任何使用可能导致在最低支持的iOS版本上崩溃的API现在都会生成警告。如果您正确地保护调用,您可以专门为该调用禁用警告(改编自 Apple 的SDK 兼容性指南):
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
if ([UIPrintInteractionController class]) {
// Create an instance of the class and use it.
}
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
else {
// Alternate code path to follow when the
// class is not available.
}
.pch
文件中,但是在构建时它立即在苹果框架中创建了大量错误。主要是“指定的参数数量错误”。Xcode 4.2构建iOS 3.1.3。 - nevan king#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
__attribute__((weak_import,deprecated)))
GCC不会对其“deprecated”属性接受参数,因此本文的代码将导致构建失败。 - Phil Calvin#import <Availability.h>
#import <Foundation/NSObjCRuntime.h>
#undef CF_AVAILABLE
#undef CF_AVAILABLE_MAC
#undef CF_AVAILABLE_IOS
#undef CF_DEPRECATED
#undef CF_DEPRECATED_MAC
#undef CF_DEPRECATED_IOS
#undef CF_ENUM_AVAILABLE
#undef CF_ENUM_AVAILABLE_MAC
#undef CF_ENUM_AVAILABLE_IOS
#undef CF_ENUM_DEPRECATED
#undef CF_ENUM_DEPRECATED_MAC
#undef CF_ENUM_DEPRECATED_IOS
#undef NS_AVAILABLE
#undef NS_AVAILABLE_MAC
#undef NS_AVAILABLE_IOS
#undef NS_DEPRECATED
#undef NS_DEPRECATED_MAC
#undef NS_DEPRECATED_IOS
#undef NS_ENUM_AVAILABLE
#undef NS_ENUM_AVAILABLE_MAC
#undef NS_ENUM_AVAILABLE_IOS
#undef NS_ENUM_DEPRECATED
#undef NS_ENUM_DEPRECATED_MAC
#undef NS_ENUM_DEPRECATED_IOS
#undef NS_AVAILABLE_IPHONE
#undef NS_DEPRECATED_IPHONE
#undef NS_CLASS_AVAILABLE
#undef NS_CLASS_DEPRECATED
#undef NS_CLASS_AVAILABLE_IOS
#undef NS_CLASS_AVAILABLE_MAC
#undef NS_CLASS_DEPRECATED_MAC
#undef NS_CLASS_DEPRECATED_IOS
//CF macros redefinition
#define CF_AVAILABLE(_mac, _ios) __OSX_AVAILABLE_STARTING(__MAC_##_mac, __IPHONE_##_ios)
#define CF_AVAILABLE_MAC(_mac) __OSX_AVAILABLE_STARTING(__MAC_##_mac, __IPHONE_NA)
#define CF_AVAILABLE_IOS(_ios) __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_##_ios)
#define CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_##_macIntro, __MAC_##_macDep, __IPHONE_##_iosIntro, __IPHONE_##_iosDep)
#define CF_DEPRECATED_MAC(_macIntro, _macDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_##_macIntro, __MAC_##_macDep, __IPHONE_NA, __IPHONE_NA)
#define CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_NA, __MAC_NA, __IPHONE_##_iosIntro, __IPHONE_##_iosDep)
#define CF_ENUM_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
#define CF_ENUM_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac)
#define CF_ENUM_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
#define CF_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define CF_ENUM_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define CF_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
//NS macros redefinition
#define NS_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
#define NS_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac)
#define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
#define NS_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
#define NS_ENUM_AVAILABLE(_mac, _ios) CF_ENUM_AVAILABLE(_mac, _ios)
#define NS_ENUM_AVAILABLE_MAC(_mac) CF_ENUM_AVAILABLE_MAC(_mac)
#define NS_ENUM_AVAILABLE_IOS(_ios) CF_ENUM_AVAILABLE_IOS(_ios)
#define NS_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_ENUM_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
#define NS_AVAILABLE_IPHONE(_ios) CF_AVAILABLE_IOS(_ios)
#define NS_DEPRECATED_IPHONE(_iosIntro, _iosDep) CF_DEPRECATED_IOS(_iosIntro, _iosDep)
#define NS_CLASS_AVAILABLE(_mac, _ios) __attribute__((visibility("default"))) NS_AVAILABLE(_mac, _ios)
#define NS_CLASS_DEPRECATED(_mac, _macDep, _ios, _iosDep, ...) __attribute__((visibility("default"))) NS_DEPRECATED(_mac, _macDep, _ios, _iosDep, __VA_ARGS__)
#define NS_CLASS_AVAILABLE_IOS(_ios) NS_CLASS_AVAILABLE(NA, _ios)
#define NS_CLASS_AVAILABLE_MAC(_mac) NS_CLASS_AVAILABLE(_mac, NA)
#define NS_CLASS_DEPRECATED_MAC(_macIntro, _macDep, ...) NS_CLASS_DEPRECATED(_macIntro, _macDep, NA, NA, __VA_ARGS__)
#define NS_CLASS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) NS_CLASS_DEPRECATED(NA, NA, _iosIntro, _iosDep, __VA_ARGS__)
deprecated
属性不像clang的那样带有消息参数,因此在每个文件中传递一个产生编译器错误的消息。ProjectName-Prefix.pch
文件顶部,以便在使用可能不适用于所有目标版本的API时获得警告:#import <Availability.h>
#undef __AVAILABILITY_INTERNAL_WEAK_IMPORT
#ifdef __clang__
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
__attribute__((weak_import,deprecated("API newer than Deployment Target.")))
#else
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
__attribute__((weak_import,deprecated))
#endif
正如Ben所说,如果你是有意这样做的(可能是通过在运行时检查选择器),你可以使用以下结构隐藏警告:
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- (void)conditionallyUseSomeAPI {
// Check for and use the appropriate API for this iOS version
}
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
遗憾的是,在i686-apple-darwin10-llvm-gcc-4.2(GCC)4.2.1
中,您无法在函数内部执行此操作。
最新的Xcode与其他答案不兼容。这个对我有用(只关注UIKit问题)。
原因是较新的clang版本具有内置的可用性属性。
#define TESTING_COMPILATION_TARGET
// only enable when trying to diagnose what APIs are being inappropriately used
#ifdef TESTING_COMPILATION_TARGET
#import <Availability.h>
#define __MYUNSUPPORTED __attribute((deprecated("API version unsupported")))
#define __MYUNSUPPORTED_IOS_NA __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_2_0
#define __MYUNSUPPORTED_IOS_2_1
#define __MYUNSUPPORTED_IOS_2_2
#define __MYUNSUPPORTED_IOS_3_0
#define __MYUNSUPPORTED_IOS_3_1
#define __MYUNSUPPORTED_IOS_3_2
#define __MYUNSUPPORTED_IOS_4_0
#define __MYUNSUPPORTED_IOS_4_1
#define __MYUNSUPPORTED_IOS_4_2
#define __MYUNSUPPORTED_IOS_4_3
#define __MYUNSUPPORTED_IOS_5_0
#define __MYUNSUPPORTED_IOS_5_1
#define __MYUNSUPPORTED_IOS_6_0
#define __MYUNSUPPORTED_IOS_6_1
#define __MYUNSUPPORTED_IOS_7_0
#define __MYUNSUPPORTED_IOS_7_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_0 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_2 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_3 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_4 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_0 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_2 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_3 __MYUNSUPPORTED
#import <Foundation/Foundation.h>
#undef CF_AVAILABLE
#define CF_AVAILABLE(_mac, _ios) __MYUNSUPPORTED_IOS_##_ios
#undef NS_AVAILABLE
#define NS_AVAILABLE(_mac, _ios) __MYUNSUPPORTED_IOS_##_ios
#undef CF_AVAILABLE_IOS
#define CF_AVAILABLE_IOS(_ios) __MYUNSUPPORTED_IOS_##_ios
#undef NS_AVAILABLE_IOS
#define NS_AVAILABLE_IOS(_ios) __MYUNSUPPORTED_IOS_##_ios
#endif // testing
#import <UIKit/UIKit.h>
它没有集成到工具集中。测试的一个选项是创建一个运行时检查,该检查将在开发过程中断言(在较新版本的操作系统中运行时)。
assert([<CLASS> instancesRespondToSelector:@selector(potato)]);
然后将其添加到您的库初始化程序之一即可。
您还可以创建一个脚本,用于计算特定翻译发出的警告数量 - 如果涉及的警告计数发生更改,则需要进行更新。
不,没有这样的警告。然而,当你使用新的API(因为显然你是在后来编写这些代码)时,只需查看文档以获取可用性。
另外,如果你支持3.0并且使用新的SDK进行开发,你必须绝对要在运行3.0的实际设备上进行测试。
另一件事情是你可以编写自己的实用程序,解析头文件中的可用性宏,并在调用任何不应该调用的内容时发出警告。
然而,我必须再次强调,如果你的目标是较旧的版本并且使用较新的SDK,你必须查看文档以了解API何时可用,并进行适当的测试。
[NSThread currentThread] setPriority:1.0]
看起来毫不起眼,没有像是只有在 4.0 版本才有的 API,但事实确实如此。API diffs 中一些较小的更改/添加与新的类或框架无关,它们只是从 MacOS X 多年来可用的内容中新增的。将这些全部记住并不是“专业”行为,这只是无用的死记硬背,而在每次打代码或提交版本控制之前查找每个 API 调用是浪费时间的做法。作为一个专业人士,我知道哪些框架/类何时引入,我想要抓住这些小小的坑点调用。 - Ben S