如何将“C++ 完成处理程序”传递给 NSURLSessionDataTask 以处理它返回的数据?
Posted
技术标签:
【中文标题】如何将“C++ 完成处理程序”传递给 NSURLSessionDataTask 以处理它返回的数据?【英文标题】:How can I pass a "C++ completion handler" to NSURLSessionDataTask to process the data it returned? 【发布时间】:2015-02-02 22:04:49 【问题描述】:我们的应用程序主要是用 C++ 编写的,其核心是多个平台(Win、Mac、android、ios)共享的静态库。
我们正在添加 iOS 支持,并拥有一系列使用 libcurl 的函数来执行与我们服务器的所有 HTTP get/post 通信。
但对于 iOS,我们现在使用 NSURLSession 实现这些调用。
我的问题很简单,向 NSURLSessionDataTask 提供 C++ 完成处理程序的代码看起来如何?
也许我想错了,但这是我能想到的最清楚的问题。
使用世界上的示例代码,我尝试了以下方法,但无济于事。完成处理程序中的 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());
【问题讨论】:
【参考方案1】:如果我理解您的问题,您希望 get(std::string)
在完成提取后“给您回电”。如果您愿意更改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 的块功能也不太熟悉,所以要小心我的语法。
【讨论】:
是的,太好了!你得到了我想问的问题......我不确定 std::function 是否是正确的类型,就像我正常传递 C++ 回调的情况一样。非常感谢您的详细回复! @A.G.很高兴我能帮上忙。我想拥有std::function
参数的优点之一是您可以将std::bind
的结果传递给get
,这意味着callback
的目标几乎是无限的【参考方案2】:
您似乎误解了dataTaskWithURL:completionHandler:
的工作原理;它是异步的(与大多数网络操作一样),这意味着任务将在单独的线程上运行,而不是阻塞调用线程。
这意味着您将在您的NSURLSessionDataTask
完成之前从get(..)
返回。
如果您想进行同步网络调用,您必须阻塞或忙等待,直到填充 returnVal
。我已经按照this class 的引导修改了您的obj-c 代码以使用dispatch_semaphore_t
s。
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);
【讨论】:
对,不,我明白了。我应该更清楚...... C++ 中的completionHnadler 看起来像什么,我可以传递给它以在dataTask 返回时调用它?以上是关于如何将“C++ 完成处理程序”传递给 NSURLSessionDataTask 以处理它返回的数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何将对象数组作为道具传递给组件,然后将数组的成员作为道具传递给嵌套组件?
我在监视触摸事件的 UITableViewCell 中有一个 UILabel,如何将一些事件传递给单元格,将其他事件传递给标签?