我们的应用程序主要使用C ++编写,其核心是由多个平台(Win,Mac,Android,iOS)共享的静态库。
我们正在添加iOS支持,并拥有一系列使用libcurl的功能,这些功能可以执行与我们服务器的所有HTTP get / post通信。
但对于iOS,我们现在正在使用NSURLSession实现这些调用。
我的问题非常简单,代码如何为NSURLSessionDataTask提供C ++ completionHandler?
也许我以错误的方式思考这个问题,但这是我能想到的最清楚的问题。
在世界上使用示例代码,我尝试了以下,但无济于事。完成处理程序中的Objective-C ++代码永远不会被调用。它返回到C ++代码并且永远不会返回。
这是Objective-C ++函数:
std::string get(std::string urlStr)
{
NSURLSession *defaultSession = [NSURLSession sharedSession];
NSURL * url = [NSURL URLWithString:[NSString stringWithUTF8String:urlStr.c_str()]];
__block std::string returnVal;
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"Data = %@",text);
returnVal = std::string([text UTF8String]);
}
}];
[dataTask resume];
return returnVal;
}
这就是我在C ++中调用该代码的方式:
response = get(url);
fprintf(stderr, "Data in C++: %s", response.c_str());
答案 0 :(得分:4)
如果我理解了您的问题,那么您需要get(std::string)
给您回电话#34;什么时候取完。如果您愿意更改get
的界面,那么一种方法是将std::function
作为参数传递...
void get(std::string urlStr, std::function<void(std::string)> callback)
{
...
NSURLSessionDataTask* dataTask =
[defaultSession
dataTaskWithURL:url
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"Data = %@",text);
// invoke the callback here...
callback(std::string([text UTF8String]));
}
}];
...
}
这样就可以这样使用了......
void callbackFunc(std::string data)
{
...
}
get("...", callbackFunc);
另一种构建方法是使用c ++的新std::promise
和std::future
类型......
std::future<std::string> get(std::string urlStr)
{
...
__block std::promise<std::string> p{};
NSURLSessionDataTask* dataTask =
[defaultSession
dataTaskWithURL:url
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"Data = %@",text);
// Set the result value here...
p.set_value(std::string([text UTF8String]));
}
}
];
...
return p.get_future();
}
然后就可以这样使用......
auto result = get("...");
// other processing here ...
...
auto result_value = result.get();
注意:我应该指出我已经假设使用了c ++ 11/14。如果您正在使用c ++ 03,那么您仍然可以使用boost::function
或裸函数指针 - void(*)(std::string)
来使用第一种方法。我对OS X的块功能也不太熟悉,所以要小心我的语法。
答案 1 :(得分:0)
你似乎误解了dataTaskWithURL:completionHandler:
的工作方式;它是异步的(和大多数网络操作一样),这意味着任务将在一个单独的线程上运行而不会阻塞调用线程。
这意味着您将在get(..)
完成之前从NSURLSessionDataTask
返回。
如果要进行同步网络呼叫,则必须阻止或忙碌等待,直到填充returnVal
为止。我已经修改了您的obj-c代码,以this class为首导使用dispatch_semaphore_t
。
std::string get(std::string urlStr)
{
NSURLSession *defaultSession = [NSURLSession sharedSession];
NSURL * url = [NSURL URLWithString:[NSString stringWithUTF8String:urlStr.c_str()]];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block std::string returnVal;
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"Data = %@",text);
returnVal = std::string([text UTF8String]);
}
dispatch_semaphore_signal(semaphore);
}];
[dataTask resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return returnVal;
}
编辑:根据您的评论,您正在寻找一个功能指针。 请注意,以下代码完全未经测试,但至少应指向正确的方向。
void get(std::string urlStr, void (*callback)(std::string)) {
[[[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *, NSURLResponse *response, NSError *error) {
std::string returnVal;
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"Data = %@",text);
returnVal = std::string([text UTF8String]);
}
callback(returnVal);
}] resume];
}
void my_callback(std::string) {
// do stuff with the response.
}
get(my_url, my_callback);