【问题标题】:Use C++ with Cocoa Instead of Objective-C?将 C++ 与 Cocoa 一起使用而不是 Objective-C?
【发布时间】:2018-08-23 02:05:48
【问题描述】:

我想编写使用 C++ 和 Cocoa 框架的应用程序,因为 Apple 不支持 Carbon 64 位。 C++ 在 Linux 和 Windows 上的实现似乎相当普通,但在 Mac OS X 上,似乎需要额外的 Apple 特定代码(如 Obj-C 包装器)。 Apple 似乎也在强迫开发人员使用 Objective-C 而不是 C++ 编写代码,尽管我可能是错的。

我正在尝试找到一种在 Mac 上编写代码的方法,该方法易于跨平台保持。必须在 Linux/Windows 上用 C++ 编写代码,然后在 Objective-C 中重写大部分代码会非常低效。

有没有一种方法可以用 C++ 编写未来支持并在 Xcode 中支持的代码?另外,如果这是可能的,我将如何在 Xcode 中混合 C++ 和 Objective-C?谢谢。

【问题讨论】:

    标签: c++ objective-c cocoa language-interoperability


    【解决方案1】:

    您不能完全用 C++ 编写 Cocoa 应用程序。 Cocoa 的许多核心技术都严重依赖于 Objective-C 的后期绑定功能,例如键值绑定、委托(Cocoa 风格)和目标动作模式。后期绑定要求非常难以在编译时绑定的类型化语言(如 C++ⁱ)中实现 Cocoa API。当然,您可以编写一个在 OS X 上运行的纯 C++ 应用程序。它只是不能使用 Cocoa API。

    因此,如果您想在其他平台上的 C++ 应用程序和基于 Cocoa 的应用程序之间共享代码,您有两种选择。首先是用C++写模型层,用Cocoa写GUI。这是一些非常大的应用程序使用的常用方法,包括Mathematica。您的 C++ 代码可以保持不变(您不需要“时髦”的苹果扩展来在 OS X 上编写或编译 C++)。您的控制器层可能会使用 Objective-C++(可能是您所指的“时髦”Apple 扩展)。 Objective-C++ 是 C++ 的超集,就像 Objective-C 是 C 的超集一样。在 Objective-C++ 中,您可以在 C++ 函数中进行 objc 样式的消息传递调用(如 [some-objc-object callMethod];)。相反,您可以从 ObjC 代码中调用 C++ 函数,例如:

    @interface MyClass {
        MyCPPClass *cppInstance;
    }
    @end
    
    @implementation MyClass
    - (id)init {
        if(self = [super init]) {
            cppInstance = new MyCPPClass();
        }
        return self;
    }
    - (void) dealloc {
        if(cppInstance != NULL) delete cppInstance;
        [super dealloc];
    }
    - (void)callCpp {
        cppInstance->SomeMethod();
    }
    @end
    

    您可以在 Objective-C 语言 guide 中找到有关 Objective-C++ 的更多信息。视图层可以是纯Objective-C。

    第二种选择是使用跨平台的 C++ 工具包。 Qt 工具包可能符合要求。跨平台工具包通常受到 Mac 用户的鄙视,因为它们没有完全正确地获得所有外观和感觉细节,而 Mac 用户期望 Mac 应用程序的 UI 得到润色。然而,Qt 的表现出人意料地好,并且根据受众和应用程序的使用情况,它可能已经足够好了。此外,您将失去一些 OS X 特定的技术,例如 Core Animation 和一些 QuickTime 功能,尽管在 Qt API 中有近似的替代品。正如您所指出的,Carbon 不会被移植到 64 位。由于 Qt 是在 Carbon API 上实现的,因此 Trolltech/Nokia 不得不将 Qt 移植到 Cocoa API 以使其与 64 位兼容。我的理解是 Qt 的下一个版本(目前在 release candiate 中)完成了这个转换并且在 OS X 上是 64 位兼容的。如果你有兴趣集成 C++,你可能想看看 Qt 4.5 的源代码和 Cocoa API。


    ⁱ 有一段时间,Apple 将 Cocoa API 提供给 Java,但该桥需要大量的手动调整,并且无法处理更高级的技术,例如上述键值绑定。目前,动态类型、运行时绑定的语言(如 Python、Ruby 等)是编写没有 Objective-C 的 Cocoa 应用程序的唯一真正选择(当然这些桥在底层使用 Objective-C)。

    【讨论】:

    • 我目前正在尝试移植我的小型 Ogre3D 应用程序,看起来非常痛苦。苹果是在试图将所有人都转换为 Objc,还是这真的是一个功能?
    【解决方案2】:

    好吧,这听起来可能很傻,但实际上我们可以编写纯 C++ 代码来为 Mac OS X 创建 GUI,但我们必须链接到 Cocoa 框架。

    /*
     * test1.cpp
     * This program shows how to access Cocoa GUI from pure C/C++
     * and build a truly functional GUI application (although very simple).
     * 
     * Compile using:
     *   g++ -framework Cocoa -o test1 test1.cpp
     *
     * that will output 'test1' binary.
     */
    
    
    #include <CoreFoundation/CoreFoundation.h>
    #include <objc/objc.h>
    #include <objc/objc-runtime.h>
    #include <iostream>
    
    extern "C" int NSRunAlertPanel(CFStringRef strTitle, CFStringRef strMsg,
                                   CFStringRef strButton1, CFStringRef strButton2, 
                                   CFStringRef strButton3, ...);
    
    
    int main(int argc, char** argv)
    {
        id app = NULL;
        id pool = (id)objc_getClass("NSAutoreleasePool");
        if (!pool)
        {
            std::cerr << "Unable to get NSAutoreleasePool!\nAborting\n";
            return -1;
        }
        pool = objc_msgSend(pool, sel_registerName("alloc"));
        if (!pool)
        {
            std::cerr << "Unable to create NSAutoreleasePool...\nAborting...\n";
            return -1;
        }
        pool = objc_msgSend(pool, sel_registerName("init"));
    
        app = objc_msgSend((id)objc_getClass("NSApplication"),
                           sel_registerName("sharedApplication"));
    
        NSRunAlertPanel(CFSTR("Testing"),
                        CFSTR("This is a simple test to display NSAlertPanel."),
                        CFSTR("OK"), NULL, NULL);
    
        objc_msgSend(pool, sel_registerName("release"));
        return 0;
    }
    

    【讨论】:

    • 这太棒了。有更复杂的例子吗?比如打开一个NSWindow?
    • test1.cpp: 在函数'int main(int, char**)': test1.cpp:26:48: 错误: 无法将'Class {aka objc_class*}' 转换为'id {又名 objc_object*}' 在初始化 id pool = objc_getClass("NSAutoreleasePool"); ^ test1.cpp:41:61: 错误:无法将 'Class {aka objc_class*}' 转换为 'id {aka objc_object*}' for argument '1' to 'objc_object* objc_msgSend(id, SEL, ...)' sel_registerName("sharedApplication")); ^
    • @Jichao 查看 Clang 与 internal Objective-C types 的兼容性 - 修复很简单:将 objc_getClass 替换为 (id)objc_getClass
    • 如何使用 std::string 设置例如。警报面板的标题?我尝试使用 c_str() 等,但没有任何效果......
    • 这在 macOS Catalina 中不再编译
    【解决方案3】:

    是的,您可以只使用 C++(即在 *.cpp 文件中编写它),甚至可以在 *.mm 文件中混合 C++ 和 Objective-C(标准 Objective-C 代码存储在 *.m 文件中)。

    当然,您仍然必须为您的用户界面使用 Objective-C 并为您的 C++ 对象创建 Objective-C 包装器。另一种选择是切换到Qt,它是一个支持 Windows、Mac OS X 和 Linux 的 C++ 框架——将在 LGPL 下发布,下一个版本为 4.5。

    【讨论】:

    • 请注意,如果您使用 Qt,您的应用程序会很糟糕。基于 Qt 的应用程序在外观和感觉上都不像原生 Mac 应用程序。 (例如,请参阅 Google 地球。)
    • 彼得:这根本不是真的。基于 Qt 的应用程序的外观和感觉与原生 Mac 应用程序相同,您只需要针对每个平台进行调整,这比在每个平台上编写原生 GUI 要容易得多。
    • 迈克,你被误导了。在它们的其他缺点中,mac 上基于 Qt 的应用程序根本不使用本机控件,而 Qt 库自己完成了所有绘图。这意味着 Qt 应用程序没有获得任何用于 2D 渲染的硬件加速,它们不会与 Apple 对标准控件所做的 UI 更改保持同步,并且 Qt 应用程序无法提供 ADA 合规性或脚本性,除非你重新发明这些自己轮子。换句话说,不要尝试在 Mac 上发布 Qt 应用程序。谷歌可以侥幸逃脱:你不能。
    • 他们确实使用原生控件,这就是 Qt 有 Cocoa 和 Carbon 版本的原因。它还有其他问题,但很多人在 Mac 上发布 Qt 应用程序并且它们运行良好(并且通过一些调整完美)。
    • 仅仅使用原生控件并不意味着它看起来和感觉就像原生应用程序。每个操作系统的差异是原生的感觉。如果您调整您的应用程序以在特定平台上感受到,那么在另一个平台上就不会感觉到它是原生的。而且,在曾经抽象的层上微调小行为总是比在原生层上更难。
    【解决方案4】:

    是的,你可以混合使用。

    您需要使用 Objective-C 直接操作您的 GUI 对象并接收来自它们的通知。

    如果您将这些 Objective-C 对象放在 .mm 文件中,而不是纯粹的 Objective-C .m 文件,它们可以直接调用 C++ 逻辑。请注意,您可能会看到(很多)较旧的建议,建议使用大写的 .M 来表示 Objective-C++,但这非常不稳定,可能会让您和编译器感到困惑。

    您不需要包装每个 C++ 对象,但您的 Objective-C 代码需要包含指向它们的指针。

    Apple 不再发布任何示例来展示如何做到这一点。

    Peter Steinberger 在 Realm [Objective] C++: What Could Possibly Go Wrong? 主持了一段很棒的视频,我强烈推荐给仍在使用 Objective-C++ 的任何人,您可以快速浏览脚本。

    【讨论】:

    • @SteveS 你的链接也坏了
    • @fferi - 上面的 Steinberger 链接已修复。 Carbon Cocoa Integration 于 2007 年来自 developer.apple.com,Apple 将其删除。这表明您真的不应该使用 Carbon API 编写新代码。在这一点上,即使使用 Carbon 维护现有代码也是有风险的。如果您需要混合 C++/Objective C,请参阅此问题的已接受答案,或 this one,但您不应该使用 Carbon。也就是说,在这里:Carbon-Cocoa-Integration
    【解决方案5】:

    如果您只是想使用普通的 C++,它绝对受支持,并且与任何其他平台没有什么不同。 Xcode 甚至在 File > New Project > Command Line Utility > C++ Tool 下都有一个模板。此外,许多流行的开源库(libcurl、libxml2、sqlite 等)随 OS X 一起提供,可用于动态链接。如果您不想使用,则不必使用 Cocoa 或任何特定于 Apple 的东西。

    如果您确实想在应用的某些部分使用 Cocoa,请查看 Objective-C++。您可以在同一个文件中混合 C++ 和 Objective-C,方法是给它一个 .mm 的扩展名,或者在 Xcode 中右键单击该文件并选择 Get Info > General,然后将文件类型更改为 sourcecode.cpp.objcpp。如果您有一个 .cpp 文件,并且您想在 Mac 特定的#ifdef 中使用 Objective-C,则第二个选项很有用。

    【讨论】:

    • 顺便说一句,(真正有用的)C++ 模板已随最新版本的 Xcode(4.x 和 5.x)消失了
    【解决方案6】:

    虽然这是多年前的问题......

    我有tried to make C++ wrapper of some Cocoa classes

    这是一次非常好的体验。 C++ 提供了比 Objective-C 更好的类型安全性,并使我编写的代码更少。但是编译时间和内存安全性更差。这是可能的,但一些基于动态的功能不容易处理。我认为在 C++ 上处理它没有意义。

    无论如何,由于 Swift 的发布,我的项目最终被放弃了。它清除了我最初想使用 C++ 的所有原因,并且提供了更多更好的功能。

    【讨论】:

      【解决方案7】:

      如果您正在编写纯图形应用程序,即使用代码绘制所有内容,请考虑使用 openFrameworks。它是一种建立在 C/C++ 之上的开源图形编程语言。它有addons 允许人们扩展语言。他们有an addon for the iphone。我相信它附带的库和 XCode 项目将帮助您为 iPhone 和 iPod touch 编译应用程序。

      【讨论】:

        猜你喜欢
        • 2011-04-15
        • 1970-01-01
        • 2021-09-23
        • 1970-01-01
        • 1970-01-01
        • 2010-12-14
        • 2011-01-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多