【问题标题】:Calling C++ from Objective-C从 Objective-C 调用 C++
【发布时间】:2021-04-20 20:07:05
【问题描述】:

各位已经成功从Objective-C调用C++代码的朋友,请赐教?

link 表示您需要使用他在他的文章中描述的技术来包装您的 C++ 代码。看起来不错,但我还是有问题。

link 表示只要调用 C++ 类的 Objective-C 类已转换为 .mm (Objective-C++) 类,那么两者应该可以很好地协同工作。

当我尝试添加方法调用时,这些方法中的每一种都让我感到悲伤。谁能给我一个简单的 Hello World iOS 应用程序的代码,该应用程序使用 Objective-C 作为“Hello”部分,使用 C++ 类作为“World”部分,中间有一个 Objective-C++ 类?还是我的整个概念都错了?

【问题讨论】:

  • 第二种方法是我成功使用的。更详细地描述你的悲伤。
  • 在大多数情况下,您只需将所有“必须一起工作”的代码放入一个 .mm 文件中,并根据需要来回调用。唯一真正的“陷阱”是存储管理。
  • 详细描述你的悲伤。
  • @lucky:你有什么解决办法吗? ,我还想要一个简单的 helloword 应用程序来知道它确实有效
  • @Piyushmatta - 是的,我做到了。给我一些,我会给你你正在寻找的帮助。 :-)

标签: c++ ios objective-c objective-c++


【解决方案1】:

本质上,您需要一个扩展名为 .mm 的 ObjC 类,它调用扩展名为 .mm 的 ObjC 类。第二个将用作 C++ 包装类。包装类将调用您实际的 .cpp 类。这有点棘手,所以我会给你一些详细的代码。以下是该项目的概述:

在您的 ObjC 代码 (ViewController) 中,您将调用 CplusplusMMClass

- (IBAction)buttonPushed:(UIButton *)sender {
    self.mmclass = [[CplusplusMMClass alloc]init];  // bad practice; but showing code
    NSString *str = [self.mmclass fetchStringFromCplusplus];
    [self populateLabel:str];
}

这里是 CplusplusMMClass .h 和 .mm

#import <Foundation/Foundation.h>
#import "WrapperClass.h"

@interface CplusplusMMClass : NSObject
@end

@interface CplusplusMMClass()
@property (nonatomic, strong) WrapperClass *wrapper;
- (NSString*)fetchStringFromCplusplus;
@end

#import "CplusplusMMClass.h"
#import "WrapperClass.h"

@implementation CplusplusMMClass

- (NSString*)fetchStringFromCplusplus {
    self.wrapper = [[WrapperClass alloc] init];
    NSString * result = [self.wrapper getHelloString];
    return result;
}

@end

这里是 WrapperClass .h 和 .mm

#ifndef HEADERFILE_H
#define HEADERFILE_H

#import <Foundation/Foundation.h>

#if __cplusplus

#include "PureCplusplusClass.h"

@interface WrapperClass : NSObject
@end

@interface WrapperClass ()
- (NSString *)getHelloString;
@end


#endif
#endif

#import "WrapperClass.h"

#include "WrapperClass.h"
#include "PureCplusplusClass.h"

using namespace test;

@interface WrapperClass ()
@property (nonatomic) HelloTest helloTest;
@end

@implementation WrapperClass

- (NSString *)getHelloString {
    self.helloTest = *(new HelloTest);
    std::string str = self.helloTest.getHelloString();
    NSString* result = [[NSString alloc] initWithUTF8String:str.c_str()];
    return result;
}

@end

这里是 PureCplusplusClass .h 和 .cpp

#ifndef __HelloWorld__PureCplusplusClass__
#define __HelloWorld__PureCplusplusClass__

#include <stdio.h>
#include <string>

using namespace std;

namespace test {
    class HelloTest
    {
    public:
        std::string getHelloString();
    };
}

#endif /* defined(__HelloWorld__PureCplusplusClass__) */

#include <stdio.h>
#include <string>

std::string test::HelloTest::getHelloString() {
    std::string outString = "Hello World";
    return outString;
}

这段代码并不完美!我在识别命名空间测试时遇到问题。有空我会更新的。

但这应该能让你到达那里!!!!

【讨论】:

  • 亲爱的!您可以在任何地方上传您的项目并给我链接吗?我试图重复 Ctrl+C -> Ctrl+V 并且我有一些错误:架构 x86_64 的未定义符号:“test::HelloTest::getHelloString()”,引用自:WrapperClass.o ld 中的 -[WrapperClass getHelloString] :未找到架构 x86_64 的符号 clang:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用) 我怀疑我需要插入一些框架。您对此有何看法?
  • 嗨,安德鲁:您收到的错误表明问题出在您的架构设置或链接方式上。我创建了一个新项目,因为上面的代码有点过时了。显然我们不再需要使用“#if __cplusplus”,C++ 的#ifndef 和#define 是不同的。新项目位于此处:https://drive.google.com/open?id=0B8tC1ewFw3KlZ2RZM28yQmhPRTg。如果您仍然遇到问题,请查看此问题的答案:stackoverflow.com/q/7533321/1735836
  • Andrew:如果您仍有问题并且找不到答案,请告诉我。我很好奇。
  • @Patricia 嗨我有同样的问题,我从谷歌驱动器下载了你的项目,但我无法运行它,因为它没有情节提要。"/Users/xinruchen/Downloads/HelloWorld/HelloWorld /Base.lproj/LaunchScreen.storyboard:Interface Builder 无法打开文档 LaunchScreen.storyboard”,因为它不存在。” thx
  • @TonyChen - 我已经好几年没看过这个项目了,我不知道有多少变化,但我会在这周的某个时候尝试看看它。
【解决方案2】:

另一个(人为的)一个:

使用 C++ 类作为 ivar:

文件 Foo.h

#import <Foundation/Foundation.h> 

@interface Foo : NSObject
@property (nonatomic, readonly) NSString* what;
@end

文件:Foo.mm

#import "Foo.h"
#include <string>

@implementation Foo {
    std::string _string;  // C++ class must have a default c-tor
}

- (id)init
{
    self = [super init];
    if (self) {
        _string = "Hello, World!"
    }
    return self;
}

- (NSString*) what {
    NSString* result = [[NSString alloc] initWithBytes:_string.data()
                                                length:_string.size() 
                                              encoding:NSUTF8StringEncoding];
    return result;
}

@end

注意:

可执行文件可能需要显式链接到 C++ 库,例如通过向“其他链接器标志”添加一个附加标志:-lc++

或者,main.m 文件可以重命名为 main.mm

后者在选择“正确”库方面更加稳健,因为工具链会自行完成。但也许,对于其他检查项目的人来说,重命名的“main.m”可能并不那么明显,可能会引起混淆。

【讨论】:

    【解决方案3】:

    我在使用 xcode 12.4 时遇到了这种情况,需要在 Objective-c 项目中使用现有的 C++ 类“CppModule”。这就是我所做的。

    1. 第一个问题是 CppModule.hpp 中的#include 给出了编译器错误:找不到“字符串”文件。通过将 .hpp 文件内容包装在
    2. 中解决了这个问题
    #if defined __cplusplus
      declarations
    #endif
    
    1. 然后字符串 var 声明给出了另一个编译器错误:未知类型名称“字符串”;你的意思是'std :: string'吗?通过以下方式修复:
    #include <string> 
    using namespace std;
    
    1. C++ CppModule.cpp、CppModule.hpp 包含文件、调用objective-c module.m,以及它的module.h 文件必须都设置为“Objective-C++ Source”类型,使用 xcode 的检查器(编辑器窗口的右上角)。将 module.m 重命名为 module.mm 还将类型设置为“Objective-C++ Source”(xcode 12.4)。使用 .mm 可能很好,提醒一下它调用 C++。

    在 module.h 文件中包含 C++ 头文件:

    #include < CppModule.hpp>
    
    1. 奇怪(对我而言)是 module.h 文件中的“CppModule”未被识别为已知类型,因此您不能在 module.h 中声明该类型的变量或属性,而是在 module.m( m) 文件它是已知类型。在 module.mm 文件中,在 @implementation 之后添加:
    static CppModule *module_cpp;
    
    1. 现在您可以,例如在module.mm的init中,调用C++模块的初始化器,如:
    module_cpp = new CppModule(parameters);
    

    C++ 模块仍然可以通过静态 module_cpp *pointer 访问。

    1. 在你的objective-c方法中调用module.cpp的方法(例子):
    CppModule::Result r = module_cpp->CppMethod();
    

    看起来工作得很好!

    如果有人能解释 4 中的奇怪之处,我将不胜感激。

    我需要的模块只是进行一些复杂的(数字)计算,而我不想将该代码转换为 Objective-C。

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 2012-01-10
      • 1970-01-01
      • 2011-05-26
      • 2012-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-12
      相关资源
      最近更新 更多