如何将 "C++ completion handler" 传递给 NSURLSessionDataTask 以处理它返回的数据?
How can I pass a "C++ completion handler" to NSURLSessionDataTask to process the data it returned?
我们的应用主要是用C++写的,核心是多平台共享的静态库(Win,Mac,Android,iOS)。
我们正在添加 iOS 支持,并且有一系列使用 libcurl 的函数,这些函数执行与我们服务器的所有 HTTP get/post 通信。
但是对于 iOS,我们现在使用 NSURLSession 实现这些调用。
我的问题很简单,向 NSURLSessionDataTask 提供 C++ completionHandler 的代码看起来如何?
也许我想的不对,但这是我能想到的最清楚的问题。
使用世界上的示例代码,我尝试了以下方法,但无济于事。永远不会调用完成处理程序中的 Objective-C++ 代码。它 returns 到 C++ 代码而不是 returns.
这是 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());
您似乎误解了 dataTaskWithURL:completionHandler:
的工作原理;它是异步的(与大多数网络操作一样),这意味着该任务将在单独的线程上运行并且不会阻塞调用线程。
这意味着您将在 NSURLSessionDataTask
完成之前从 get(..)
return。
如果要进行同步网络调用,则必须阻塞或忙等到 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);
如果我理解你的问题,你想要 get(std::string)
到 "call you back" 完成提取。如果您愿意更改 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 的块功能,因此请注意我的语法。
我们的应用主要是用C++写的,核心是多平台共享的静态库(Win,Mac,Android,iOS)。
我们正在添加 iOS 支持,并且有一系列使用 libcurl 的函数,这些函数执行与我们服务器的所有 HTTP get/post 通信。
但是对于 iOS,我们现在使用 NSURLSession 实现这些调用。
我的问题很简单,向 NSURLSessionDataTask 提供 C++ completionHandler 的代码看起来如何?
也许我想的不对,但这是我能想到的最清楚的问题。
使用世界上的示例代码,我尝试了以下方法,但无济于事。永远不会调用完成处理程序中的 Objective-C++ 代码。它 returns 到 C++ 代码而不是 returns.
这是 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());
您似乎误解了 dataTaskWithURL:completionHandler:
的工作原理;它是异步的(与大多数网络操作一样),这意味着该任务将在单独的线程上运行并且不会阻塞调用线程。
这意味着您将在 NSURLSessionDataTask
完成之前从 get(..)
return。
如果要进行同步网络调用,则必须阻塞或忙等到 returnVal
被填充。在 this class.
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);
如果我理解你的问题,你想要 get(std::string)
到 "call you back" 完成提取。如果您愿意更改 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 的块功能,因此请注意我的语法。