【问题标题】:Why does objective-c not have API availability checking?为什么objective-c没有API可用性检查?
【发布时间】:2016-03-04 22:56:42
【问题描述】:

Swift 2 有 API availability checking

当使用对您来说太新的 API 时,编译器会给您一个错误 最小目标操作系统

为什么 Objective-c 编译器不能做同样的事情?

我在 Google 上搜索了 Objective c API 可用性检查,结果只有 swift 2 个结果,所以我认为 Objective c 的编译器无法做到这一点。

【问题讨论】:

  • Swift 在设计时就考虑到了这一特性,因此 Apple 可以添加该特性而不会造成不兼容或妨碍语言的通常功能。 Objective-C 是非常动态的,在编译时很难判断在运行时什么可用或不可用。
  • 看看developer.apple.com/library/ios/documentation/DeveloperTools/… 你基本上可以通过调整你的Base SDK和部署目标来实现同样的事情,但它更麻烦。
  • 你可以使用respondsToSelector:@selector(methodName)来检查它的可用性,因为没有像swift这样的专用机制。
  • SDK 版本中不可用的方法或类与动态调度无关。当然,Objective-C 编译器会警告你没有这样的声明。与 Swift 相比,您可以在编译时在运行时对此类错误做出反应。与 Swift 相比,Objective-C 不需要任何特殊功能。有人可能认为这是不那么复杂的。有人可能会认为这是语言力量的标志。

标签: objective-c llvm-clang


【解决方案1】:

现在确实如此。此外,使用 Xcode 11(包括当前的 Xcode 11.3.1),您甚至可以从 sn-ps 获取它。按 Xcode 右上角的 + 按钮(如下图所示)。

然后在搜索框中输入“API”。所有 3 个用于 API 可用性检查的 sn-p 版本都将出现——Objective C、C 和 Swift。

【讨论】:

    【解决方案2】:

    Xcode 9.0 将运行时可用性检查语法从 Swift 引入到 Objective-C:

    if (@available(macOS 10.9, *))
    {
    // call 10.9+ API
    }
    else
    {
    // fallback code
    }
    

    这包含调用比您的部署目标更新的 API 的警告(如果这些调用未包含在检查中)。

    终于;)

    【讨论】:

    • 是运行时检查吗?
    【解决方案3】:

    警告(Swift 使其成为错误)已经多年没有在 Clang 编译器中实现,但它不是 Objective-C 固有的限制(尽管由于其动态特性,您将无法捕捉到所有情况),也不是 Swift 术语。

    使用可用性信息注释标头的 Apple 宏(例如,NS_CLASS_AVAILABLE)和源属性(__attribute__((visibility(...)))__attribute__((availability(...))))已经存在多年,并且在 Apple 的 SDK 中得到广泛使用。宏在FoundationNSObjCRuntime.hAvailability.h/AvailabilityMacros.h 系统头文件中定义,编译器可以(并且确实)读取它们。

    在 2015 年初,-Wpartial-availability 警告 has been added 到 Clang 的 master 分支,但这个提交/警告直到(包括)Xcode 7.2 才进入 Apple 的 Clang 版本。在 Xcode 7.2 中将警告标志添加到项目时,您将获得 unknown warning option 日志,但该标志在 Xcode 7.3 中可用。目前没有针对它的预定义设置,但您可以在 Build Settings 下将标志添加到 Other Warning Flags

    还有其他工具使用 LLVM 库来检测部分可用的 API,例如 Deploymate。对于我的毕业论文,我开发了一个直接集成到 Xcode 中的工具,它基于对 Clang 编译器的修改。代码仍然是online,但是我没有跟上一般的Clang开发,所以除了学习目的外,它没有多大用处。但是,“官方”代码(上面链接)更简洁更好。

    编辑:从 Xcode 9 开始,可用性检查也适用于 Objective-C(和 C)。而不是使用上述不支持临时/本地提高部署目标并因此导致大量误报的警告标志,有-Wunguarded-availabilityif (@available(iOS 11, *)) {...}来检查并提高以下块的部署目标代码。它默认关闭,但-Wunguarded-availability-new 默认开启,并开始检查 iOS/tvOS 11、watchOS 4 和 High Sierra 之外的任何内容。有关这方面的更多详细信息,请参阅 Xcode 9 beta release notes,目前需要使用开发者帐户登录。

    【讨论】:

    • 感谢您链接 deploymate 和您的论文。看起来很有趣。
    • @hagi。感谢关于部分可用性标志的观点。我能够将最新的 clang 与 Xcode 7.2 连接并收到警告。 gist.github.com/vigneshr89/befe6a99e7979772e449
    • Xcode 7.3 测试版似乎带有一个支持开箱即用标志的 Clang 版本 :)
    【解决方案4】:

    Objective C 没有 可用性检查 作为语言的一部分,因为通过 Objective C 预处理器可以获得相同的结果。 这是 C 派生语言的“传统”方式。

    想知道是否在调试模式下编译?

    #ifdef DEBUG
      // code which will be inserted only if compiled in debug mode
    #endif 
    

    想在编译时检查最低版本吗? 在 iOS 中使用 Availability.h 标头,在 Mac OS X 中使用类似标头。

    此文件位于 /usr/include 目录中。

    只需使用预处理器测试 __IPHONE_OS_VERSION_MAX_ALLOWED,例如:

    #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
                if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
                    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil]];
                }else{
                    [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert)];
                }
    #else
                [[UIApplication sharedApplication] registerUserNotificationSettings: (UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert)];
    #endif
    

    由于 Swift 没有预处理器,他们必须发明一种在语言本身内进行此类检查的方法。

    如果您想在运行时检查方法的可用性,请注意适当的方法是使用方法 respondsToSelector:instancesRespondToSelector:(后者位于等级)。

    您通常希望将两种方法结合起来,即编译时条件编译和运行时检查。

    Objective C 方法存在验证,例如在班级层面:

    if ([UIImagePickerController instancesRespondToSelector:
                  @selector (availableCaptureModesForCameraDevice:)]) {
        // Method is available for use.
        // Your code can check if video capture is available and,
        // if it is, offer that option.
    } else {
        // Method is not available.
        // Alternate code to use only still image capture.
    }
    

    如果要在运行时测试一个C函数是否存在,那就更简单了:如果存在,则函数本身不为null。

    您不能在两种语言中使用相同的方法。

    【讨论】:

      【解决方案5】:

      当然,您会在 Objective-C 代码中遇到错误。但是,如果您使用为 Swift 定义的术语,您将不会在 google 中找到 Objective-C 的结果,因为如果您搜索德语单词,您将不会在 google 中找到 kisuaheli 网站。 ;-)

      将 Objective-C 代码与太旧的 SDK 链接时会出错。这仅仅是因为使用的方法或类或 $whatever 未在该 SDK 的标头中定义。再次,当然。

      这是 Apple 的典型 Swift 营销:由于 Swift 的无能,他们必须扩展语言才能得到一些东西,这在 Objective-C 中很容易。他们没有澄清这是 Swift 的不足造成的,而是告诉你这是 Swift 的一个很棒的特性。这就像割断你的手指然后说:“我们有很棒的石膏功能!!!!!!!!!”而且您只需要等待几天,就会出现一个带有问题的 SO:“为什么 Objective-C 没有出色的石膏功能???????”简单的答案:它不会割伤你的手指。

      问题是不是产生错误。问题是所有版本都有一个源代码,因此您可以简单地更改 SDK 版本并获取新代码(或错误)。您需要它以便于维护。

      在 Objective-C 中,您只需使用此处找到的答案: Conditionally Hide Code from the Compiler 或者您可以在运行时执行此操作,如 Q 的 cmets 中所述。(但这本质上是该问题的不同解决方案,因为它是一种动态方法,而不是您在 Swift 中必须做的静态方法。 )

      在 Swift 中具有语言功能的原因是 Swift 没有预处理器,因此 Objective-C 解决方案在 Swift 中不起作用。因此条件代码在 Swift 中是不可能的,他们必须添加石膏,呃,语言功能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-30
        • 2012-01-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多