有没有办法让Xcode警告新的API调用?

问题描述 投票:25回答:9

我不止一次看到崩溃的错误出现在iOS 3.x上,原因是使用了4.x中引入的新调用,没有经过适当的检查。

有没有办法让Xcode警告只有比部署目标更高版本的类,方法和过程?

这样我就可以轻松地列出所有代码,并确保它是正确的条件化。

iphone xcode ios iphone-sdk-3.0 warnings
9个回答
27
投票

我实际上发布了一些有助于测试此类事情的东西。它是我的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>

然后它会警告(可能有一个奇怪的弃用警告)关于正在使用的API对于你设置为#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED的“soft max”的目标来说太新了。此外,如果您没有定义__IPHONE_OS_VERSION_SOFT_MAX_REQUIRED,那么它默认为您的部署目标。

我发现它很有用,因为我可以仔细检查我正在使用的哪些API对于我设置的部署目标来说太新了。


15
投票

至少在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上测试过。


15
投票

如果您使用XCode7.3及更高版本,则可以设置其他警告标志:-Wpartial-availability,然后xcode将显示比部署目标版本更新的API警告enter image description here


12
投票

在通过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 Compatibility Guide修改):

#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.
    }

4
投票

这是基于Ben S's的答案,但包含对GCC和LLVM-GCC的支持。 GCC的deprecated属性doesn't take a message argument就像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中。


4
投票

要使其在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__)

0
投票

它没有集成到工具集中。测试这个的一个选项是创建一个运行时检查,它将断言(在开发期间运行在较新版本的操作系统中)。

assert([<CLASS> instancesRespondToSelector:@selector(potato)]);

然后将其添加到您的库的初始化例程之一。

您还可以创建一个脚本,该脚本将计算针对特定翻译发出的警告的数量 - 如果有问题的警告计数发生更改,则您需要进行更新。


0
投票

最新的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>

-4
投票

不,没有这样的警告。但是,当您使用新API时(因为您显然是稍后编写这些API),只需检查文档何时可用。

此外,如果您支持3.0并使用新的SDK进行开发,则必须绝对在运行3.0的实际设备上进行测试

您可以做的另一件事是编写自己的实用程序来解析标头中的可用性宏,然后在您调用任何不应该调用的内容时发出警告。

但是,我必须重申,如果您要定位旧版本并使用较新的SDK,则必须检查文档以查看API何时可用,并进行适当的测试。

© www.soinside.com 2019 - 2024. All rights reserved.