Xcode 有没有办法警告新的 API 调用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4676000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a way for Xcode to warn about new API calls?
提问by Ben S
On more than one occasion I've seen crashing bugs appear on iOS 3.x due to use of a new call that was introduced in 4.x without proper checking.
我不止一次看到 iOS 3.x 上出现崩溃错误,原因是使用了 4.x 中引入的新调用而没有进行适当的检查。
Is there a way for Xcode to warn about classes, methods and procedures that are only available a later version than the deployment target?
Xcode 有没有办法警告仅在部署目标之后版本可用的类、方法和过程?
That way I could easily list through all the code and make sure it's properly conditionalized.
这样我就可以轻松地列出所有代码并确保它被正确地条件化。
采纳答案by Ben S
After digging through AvailabilityInternal.h
, I realized that all available versions above the Deployment target are tagged with the __AVAILABILITY_INTERNAL_WEAK_IMPORT
macro.
深入研究之后AvailabilityInternal.h
,我意识到部署目标之上的所有可用版本都带有__AVAILABILITY_INTERNAL_WEAK_IMPORT
宏标记。
Therefore, I can generate warnings by redefining that macro:
因此,我可以通过重新定义该宏来生成警告:
#import <Availability.h>
#undef __AVAILABILITY_INTERNAL_WEAK_IMPORT
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
__attribute__((weak_import,deprecated("API newer than Deployment Target.")))
By placing this code in a project's precompiled header, any use of an API that might cause a crash on the lowest supported iOS version now generates a warning. If you correctly guard the call, you can disable the warning specifically for that call (modified exmaple from Apple's SDK Compatibility Guide):
通过将此代码放置在项目的预编译头文件中,任何可能导致最低支持的 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.
}
回答by mattjgalloway
I've actually released something which helps with testing this sort of thing. It's part of my MJGFoundation set of class called MJGAvailability.h.
我实际上已经发布了一些有助于测试这类事情的东西。它是我的 MJGFoundation 类的一部分,称为MJGAvailability.h。
The way I've been using it is to apply it in my PCH file like this:
我一直使用它的方式是将它应用到我的 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>
Then it'll warn (with perhaps a strange deprecation warning) about APIs which are being used that are too new for the target you set as the "soft max" as per the #define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
. Also if you don't define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
then it defaults to your deployment target.
然后它会警告(可能带有奇怪的弃用警告)关于正在使用的 API 对于您根据#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
. 此外,如果您未定义,__IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
则默认为您的部署目标。
I find it useful because I can then double check which APIs I'm using that are too new for the deployment target that I've set.
我觉得它很有用,因为我可以仔细检查我使用的哪些 API 对于我设置的部署目标来说太新了。
回答by galway
回答by Daniel
On OS X at least, with recent clang/SDK, there is now a -Wpartial-availability
option (add it e.g. in "other warning options")
One can then define the following macros to encapsulate code that handles runtime testing if the method is supported
至少在 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")
I haven't test on iOS though.
不过我还没有在iOS上测试过。
回答by Phil Calvin
This is based on Ben S'sanswer, but incorporates support for GCC and LLVM-GCC. GCC's deprecated
attribute doesn't take a message argumentlike clang's, so passing one produces a compiler error in basically every file.
这是基于Ben S 的回答,但包含对 GCC 和 LLVM-GCC 的支持。GCC 的deprecated
属性不像 clang那样接受消息参数,因此传递一个参数会在基本上每个文件中产生编译器错误。
Place the following code at the top of your ProjectName-Prefix.pch
file to get a warning for every use of an API that may not be available in all your targeted versions:
将以下代码放在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
As Ben says, if you're intentionally doing this (perhaps by checking for the selector at runtime), you can hide the warning using this construct:
正如 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"
Regrettably, you can't do this inside a function, at least in i686-apple-darwin10-llvm-gcc-4.2 (GCC) 4.2.1
.
遗憾的是,您不能在函数内部执行此操作,至少在i686-apple-darwin10-llvm-gcc-4.2 (GCC) 4.2.1
.
回答by Werner Altewischer
To get this to work under XCode 5 you need to also redefine the NS_AVAILABLE and NS_DEPRECATED macros because CFAvailability.h distinguishes between compilers that support the attribute_availability_with_message feature. Copy the following above the "MJGAvailability.h" import in your precompiled header to get this to work with the new Apple LLVM compiler:
要使其在 XCode 5 下工作,您还需要重新定义 NS_AVAILABLE 和 NS_DEPRECATED 宏,因为 CFAvailability.h 区分支持 attribute_availability_with_message 功能的编译器。将以下内容复制到预编译头文件中的“MJGAvailability.h”导入上方,以使其与新的 Apple LLVM 编译器一起使用:
#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__)
回答by xtravar
Latest Xcode didn't work with other answers. This works for me (only looking for UIKit issues).
最新的 Xcode 不适用于其他答案。这对我有用(只寻找 UIKit 问题)。
The reason is that the newer clang versions have a built-in availability attribute.
原因是较新的 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>
回答by justin
it's not integrated into the toolset. one option to test this is to just create a runtime check which would assert (during development while running in newer versions of the os).
它没有集成到工具集中。测试这一点的一种选择是创建一个运行时检查,该检查会断言(在开发过程中,同时在较新版本的操作系统中运行)。
assert([<CLASS> instancesRespondToSelector:@selector(potato)]);
then just add that to one of your library's initialization routines.
然后只需将其添加到您的库的初始化例程之一。
you could also create a script which would count the number of warnings emitted for a specific translation - if the warning count in question changes then you have updates to make.
您还可以创建一个脚本来计算针对特定翻译发出的警告数量 - 如果有问题的警告数量发生变化,那么您需要进行更新。
回答by Jason Coco
No, there is no such warning. However, when you use new API (since you are obviously writing these later), just check the docs when they were available.
不,没有这样的警告。但是,当您使用新 API 时(因为您显然是稍后编写这些 API),只需在文档可用时检查它们。
Also, if you're supporting 3.0 and using new SDK for development, you must absolutely be testing on actual devices running 3.0
此外,如果您支持 3.0 并使用新的 SDK 进行开发,则您绝对必须在运行 3.0 的实际设备上进行测试
Another thing you could do is write your own utility that parses the availability macros in the headers and then warns you if you're calling anything you shouldn't be.
您可以做的另一件事是编写您自己的实用程序来解析标头中的可用性宏,然后在您调用不应该调用的任何内容时向您发出警告。
However, I must reiterate, if you're targeting an older version and using the newer SDK, you must check the docs to see when API became available, and test appropriately.
但是,我必须重申,如果您的目标是旧版本并使用较新的 SDK,则必须检查文档以查看 API 何时可用,并进行适当的测试。