从Objective-C调用C++

19

对于那些能够成功从Objective-C调用C++代码的人,你们能否启发一下我?

这个链接说你需要使用他在文章中描述的技术来包装你的C++代码。看起来不错,但我仍然有问题。

这个链接说只要调用C++类的Objective-C类已转换为.mm(Objective-C++)类,则两者应该很好地配合使用。

每种方法都在我尝试添加方法调用时引起了困扰。请问是否可以给我一个简单的Hello World iOS应用程序的代码,其中Objective-C用于“Hello”部分,C++类用于“World”部分,并使用Objective-C++类连接两者?或者我的整个概念还是错误的?


1
第二种方法是我成功使用的。请更详细地描述您的悲痛。 - trojanfoe
1
大多数情况下,您只需将所有“必须协同工作”的代码放入一个.mm文件中,并根据需要进行回调。唯一真正需要注意的是存储管理。 - Hot Licks
1
请更详细地描述您的悲痛。 - Hot Licks
@lucky:你有没有找到任何解决方案?我也想要一个简单的“Hello World”应用程序,以确保它实际可行。 - Piyush matta
@Piyushmatta - 是的,我做了。稍等一下,我会给你提供你需要的帮助。 :-) - Patricia
显示剩余2条评论
4个回答

25
基本上,您需要一个带有.mm扩展名的ObjC类来调用另一个带有.mm扩展名的ObjC类。第二个类将用作C++包装器类,该包装器类将调用您实际的.cpp类。这有点棘手,所以我会给您提供一些冗长的代码。以下是项目概述:

enter image description here

在您的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;
}

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

但这应该可以帮助你实现目标!!!


1
你好,安德鲁:你遇到的错误表明问题出在你的架构设置或链接方式上。由于上面的代码有点过时,我已经创建了一个新项目。显然我们不再需要使用“#if __cplusplus”,而C++的#ifndef和#define也有所不同。新项目位于这里:https://drive.google.com/open?id=0B8tC1ewFw3KlZ2RZM28yQmhPRTg。如果你仍然遇到问题,请参考这个问题的答案:https://dev59.com/vmsz5IYBdhLWcg3w-85v。 - Patricia
安德鲁:如果你还有问题找不到答案,请告诉我。我很好奇。 - Patricia
@Patricia 你好,我遇到了同样的问题,我从谷歌云盘下载了你的项目,但是我无法运行它,因为它没有故事板。"/Users/xinruchen/Downloads/HelloWorld/HelloWorld/Base.lproj/LaunchScreen.storyboard: Interface Builder could not open the document LaunchScreen.storyboard" because it does not exist." 谢谢。 - Tony Chen
@TonyChen - 抱歉我没能及时回复你。我工作很忙。请告诉我!其他人可能也需要这个信息。如果你提供答案,我可以点赞并且其他人也可以。 :-) - Patricia
@Patricia 噢,我好久没来这个网站了,所以我记不清了……但我想我通过另一种方式解决了我的问题,所以这并没有什么帮助。 - Tony Chen
显示剩余3条评论

2

另一个(人为制造的)例子:

将C++类用作实例变量:

文件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”可能不太明显,并可能导致混淆。


-1

Objective-C的IOS网银示例

- (IBAction)Button:(id)sender {

    NSMutableArray *userdata=[[NSMutableArray alloc]initWithObjects:@"Name",@"CreditcardNumber",@"Pinnumber",@"Active", nil]; // arrkey = userdata.

    NSMutableArray *Yogesh=[[NSMutableArray alloc]initWithObjects:@"Yogesh",@"908380637367",@"5656",@"yes", nil];
    NSMutableArray *sabari=[[NSMutableArray alloc]initWithObjects:@"Sabari",@"7635298721",@"6543",@"no",nil];
    NSMutableArray *rajesh=[[NSMutableArray alloc]initWithObjects:@"rajesh",@"8373197272",@"8765",@"yes",nil];
    NSMutableArray *Ramya =[[NSMutableArray alloc]initWithObjects:@"Ramya ",@"123456",@"9898",@"No",nil];
    NSMutableDictionary * dic= [[NSMutableDictionary alloc]initWithObjects:Yogesh forKeys:userdata];
    NSMutableDictionary * dic1=[[NSMutableDictionary alloc]initWithObjects:sabari forKeys:userdata];
    NSMutableDictionary * dic2=[[NSMutableDictionary alloc]initWithObjects:rajesh forKeys:userdata];
    NSMutableDictionary * dic3=[[NSMutableDictionary alloc]initWithObjects:Ramya  forKeys:userdata];
    /* NSLog(@"%@", dic);
     NSLog(@"%@", dic1);
     NSLog(@"%@", dic2);*/

    NSMutableArray * arraylist=[[NSMutableArray alloc]initWithObjects:dic,dic1,dic2,dic3, nil];
    NSLog(@"Array = %@", arraylist);
    NSLog(@"user entered value %d", _ccNumber.text.intValue);
    NSLog(@"pin number %d ",_pin.text.intValue);

    for(int i = 0; i< [arraylist count]; i++){
    
        NSMutableDictionary *dictionary= arraylist[i];
            
        // NSLog(@"userdata is [%d] %@",i,[dictionary objectForKey:@"Pinnumber"]);
    
        NSInteger a;
        NSInteger vx;
        NSString *b;
        NSString *str;
        a = (self.ccNumber.text.integerValue);
        vx = (self.pin.text.integerValue);
        b = ([dictionary objectForKey:@"CreditcardNumber"]);
        str = ([dictionary objectForKey:@"Pinnumber"]);
        NSInteger c = [b integerValue];
        NSInteger d = [str integerValue];
        if(a == c && vx == d){
            NSLog(@" user data is [%d] Name: %@",i,[dictionary objectForKey:@"Name"]);
            NSLog(@" user data is [%d] Card Number: %@",i,[dictionary objectForKey:@"CreditcardNumber"]);
            NSLog(@" user data is [%d] Pin Number :%@",i,[dictionary objectForKey:@"Pinnumber"]);
            NSLog(@" user data is [%d] Active: %@",i,[dictionary objectForKey:@"Active"]);
        }else{
        
            NSString *DataStatus= @"Invalid Card Number";
        
            self.dataStatusLbl.text = DataStatus;
        
            NSLog(@"%@",DataStatus);
        }
    }
}
@end

1
你好,请查看 https://meta.stackoverflow.com/editing-help 谢谢! - Eric Aya

-1

我在使用xcode 12.4时遇到了这种情况,需要在一个objective-c项目中使用现有的C++类“CppModule”。以下是我的解决方法。

  1. 首先遇到的问题是,在CppModule.hpp中使用#include会导致编译器错误:“找不到string文件”。通过将.hpp文件内容包装在
#if defined __cplusplus
  declarations
#endif
然后字符串变量声明又出现了编译器错误:未知的类型名称'string';你是不是想说'std::string'?通过以下方式解决了这个问题:
#include <string> 
using namespace std;
  1. C++的CppModule.cpp、CppModule.hpp包含文件、调用的objective-c module.m以及它的module.h文件必须全部设置为“Objective-C ++源”类型,使用xcode的Inspector(在编辑器窗口右上方)。将module.m重命名为module.mm也会将其类型设置为“Objective-C ++源”(xcode 12.4)。也许最好使用.mm作为提醒它调用了C++。

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

#include < CppModule.hpp>
第四点:对我来说有点奇怪的是,在 module.h 文件中,“CppModule” 不被识别为已知类型,因此您无法在模块的 module.h 中声明该类型的变量或属性,但在 module.m(m) 文件中它是一个已知类型。在 module.mm 文件中,在 @implementation 后添加以下内容:
static CppModule *module_cpp;
现在你可以在 module.mm 的 init 中调用 C++ 模块的初始化器,例如:
module_cpp = new CppModule(parameters);

而C ++模块仍可通过该静态module_cpp *指针访问。

  1. 在您的Objective-C方法中调用module.cpp的方法(示例):
CppModule::Result r = module_cpp->CppMethod();

看起来完全正常!

如果有人能解释第4点的奇怪之处,我会很感激。

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


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