我应该如何知道编译器是否启用了ARC支持?

13

我需要在我的iOS应用程序中编写一个库。

该语句应该预处理定义为:

myObject ...

#if ARC
   // do nothing
#else
   [myObject release]
#endif

或者作为运行时进程:

if (ARC) {
   // do nothing
} else {
   [myObject release];
}

我该怎么做?

请帮助我!谢谢。


@Sulthan,这个功能有合理的使用情况。比如确保你的库的用户不会只是获取你启用ARC的源代码并在没有ARC支持的情况下编译它们,从而在各个地方泄漏内存(我几天前就遇到了这种情况)。话虽如此,问题中给出的示例代码确实看起来很混乱。 - zoul
1
@zoul你永远无法为用户可以做的一切做好准备。在你的情况下,一个简单的断言如果ARC开启就足够了。试图让代码兼容ARC和MRC必将导致非常复杂(或混乱)的代码,并且可读性较差。而不可读的代码很难维护。 - Sulthan
同意,assert 就是我想要的。 - zoul
3个回答

28

你可以使用__has_feature,像这样:

#if __has_feature(objc_arc)
// ARC is On
#else
// ARC is Off
#endif
如果你想使用GCC进行构建(Apple的GCC不支持ARC),你可能还需要以下内容来确定编译器:
#if defined(__clang)
// It's Clang
#else
// It's GCC
#endif

更新

综合起来,它们将具有以下一般形式:

 #if defined(__clang)     

 #if !defined(__has_feature)
 // idk when clang introduced this
 #error This version of clang does not support __has_feature
 #endif

 #define MON_IS_ARC_ENABLED_IN_THIS_TRANSLATION __has_feature(objc_arc)

 #else
 // for every compiler other than clang:

 #if defined(__has_feature)
 #error Another compiler supports __has_feature
 #endif

 #define MON_IS_ARC_ENABLED_IN_THIS_TRANSLATION 0

 #endif

在你的源码中,只需使用MON_IS_ARC_ENABLED_IN_THIS_TRANSLATION或用于进一步的#define

如果你使用的编译器添加了支持,那么你需要为此添加一个case(由于它很可能禁止ref count操作的使用,因此编译器错误很可能会捕获这种情况的错误)。

请注意,这里有额外的检查,以展示如何(也应该如何)避免定义保留标识符(基于评论中的对话)。这并非详尽无遗,但是可以看作是一个演示。如果你发现自己经常编写条件性的__has_feature检查,你可能想要定义一个新的宏来减少和简化定义。


不要检查编译器身份,而是检查兼容性。使用clang推荐的#ifdef __has_feature - Jesper
@Jesper 我认为clang的建议是错误的。a) 它可能会失败(相当容易)。b) 你不应该声明编译器和/或实现保留的内容 - 具体来说,根据你提供的链接:#define __has_feature(x) 0 //与非clang编译器兼容。 - justin
@Jesper 失败是因为其他编译器(包括GCC)没有定义或支持__has_feature。在苹果系统上,它能够工作是因为CFBase.h定义了它。如果您不直接(或间接)包含CoreFoundation,则结果将是错误的 - 而且点B仍然是一个严重的问题。这里有一个真实世界的实现,支持多个编译器:http://www.boost.org/doc/libs/1_49_0_beta1/boost/type_traits/intrinsics.hpp - justin
@Justin:检查编译器特定的保留关键字是否可用,然后有选择地将其替换为noop,这不是唯一合理的方法吗?如果您检查GCC,他们会提供__has_feature怎么办? - Jesper
@Jesper 我们不应该重新定义为实现保留的内容。而且,这并不是唯一合理的方法。个人而言,我会在这里创建一个宏或 #define -- 并使用编译器定义的检查。更新中有说明(上面),也演示了如何检测对 __has_feature 定义的更改。在此不需要定义保留标识符,并且应始终避免这样做。 - justin
@Justin:这很纯粹,但我不确定它有多大用处。我们必须同意不同意见。 - Jesper

9
你可以使用宏来实现:
#if !defined(__clang__) || __clang_major__ < 3
    #ifndef __bridge
        #define __bridge
    #endif

    #ifndef __bridge_retain
        #define __bridge_retain
    #endif

    #ifndef __bridge_retained
        #define __bridge_retained
    #endif

    #ifndef __autoreleasing
        #define __autoreleasing
    #endif

    #ifndef __strong
        #define __strong
    #endif

    #ifndef __unsafe_unretained
        #define __unsafe_unretained
    #endif

    #ifndef __weak
        #define __weak
    #endif
#endif

#if __has_feature(objc_arc)
    #define SAFE_ARC_PROP_RETAIN strong
    #define SAFE_ARC_RETAIN(x) (x)
    #define SAFE_ARC_RELEASE(x)
    #define SAFE_ARC_AUTORELEASE(x) (x)
    #define SAFE_ARC_BLOCK_COPY(x) (x)
    #define SAFE_ARC_BLOCK_RELEASE(x)
    #define SAFE_ARC_SUPER_DEALLOC()
    #define SAFE_ARC_AUTORELEASE_POOL_START() @autoreleasepool {
    #define SAFE_ARC_AUTORELEASE_POOL_END() }
#else
    #define SAFE_ARC_PROP_RETAIN retain
    #define SAFE_ARC_RETAIN(x) ([(x) retain])
    #define SAFE_ARC_RELEASE(x) ([(x) release])
    #define SAFE_ARC_AUTORELEASE(x) ([(x) autorelease])
    #define SAFE_ARC_BLOCK_COPY(x) (Block_copy(x))
    #define SAFE_ARC_BLOCK_RELEASE(x) (Block_release(x))
    #define SAFE_ARC_SUPER_DEALLOC() ([super dealloc])
    #define SAFE_ARC_AUTORELEASE_POOL_START() NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    #define SAFE_ARC_AUTORELEASE_POOL_END() [pool release];
#endif

上述内容来自网站:http://raptureinvenice.com/arc-support-without-branches/;但我已经复制粘贴以确保不会丢失。

2

通常情况下,您不希望做以下这些事情:

#if ARC
   // do nothing
#else
   [myObject release]
#endif

由于这是一种灾难性的做法,这样的代码中隐藏着许多微妙的错误。但如果你确实有一个合理的用例,你最好使用宏(我不知道__has_feature,谢谢 Justin!):

#if __has_feature(objc_arc)
    #define MY_RELEASE(x) while (0) {}
#else
    #define MY_RELEASE(x) [x release]
#endif

但是,我甚至会感到很紧张,即使使用这个功能,疼痛的潜在风险非常大 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接