我不止一次看到iOS 3.x上出现崩溃的错误,原因是使用了4.x中引入的新调用而没有经过适当的检查。
Xcode是否有办法警告只有比部署目标更新版本的类,方法和程序?
通过这种方式,我可以轻松列出所有代码,并确保它已正确条件化。
答案 0 :(得分: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对于我设置的部署目标来说太新了。
答案 1 :(得分:15)
至少在OS X上,使用最近的clang / SDK,现在有一个-Wpartial-availability
选项(例如在&#34中添加;其他警告选项&#34;)
然后,可以定义以下宏来封装处理运行时测试的代码(如果支持该方法)
#define START_IGNORE_PARTIAL _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpartial-availability\"")
#define END_IGNORE_PARTIAL _Pragma("clang diagnostic pop")
我还没有在iOS上测试过。
答案 2 :(得分:15)
答案 3 :(得分: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 :(得分: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
中。
答案 5 :(得分: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__)
答案 6 :(得分:0)
它没有集成到工具集中。测试这个的一个选项就是创建一个运行时检查,它会断言(在开发过程中运行在较新版本的操作系统中)。
assert([<CLASS> instancesRespondToSelector:@selector(potato)]);
然后将其添加到库的一个初始化例程中。
您还可以创建一个脚本来计算针对特定翻译发出的警告数量 - 如果有问题的警告计数发生变化,那么您需要进行更新。
答案 7 :(得分: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>
答案 8 :(得分:-4)
不,没有这样的警告。但是,当您使用新API时(因为您显然稍后会写这些),只需检查文档何时可用。
此外,如果您支持3.0并使用新的SDK进行开发,则必须绝对在运行3.0的实际设备上进行测试
您可以做的另一件事是编写自己的实用程序来解析标题中的可用性宏,然后在您调用任何不应该调用的内容时发出警告。
但是,我必须重申,如果您要定位旧版本并使用较新的SDK,则必须检查文档以查看API何时可用,并进行相应的测试。