2

我们的应用程序主要是用 C++ 编写的,其核心是多个平台(Win、Mac、Android、iOS)共享的静态库。

我们正在添加 iOS 支持,并有一系列使用 libcurl 的函数来执行与我们的服务器的所有 HTTP get/post 通信。

但是对于 iOS,我们现在使用 NSURLSession 来实现这些调用。

我的问题很简单,代码如何为 NSURLSessionDataTask 提供 C++ 完成处理程序?

也许我正在以错误的方式思考这个问题,但这是我能想到的最清楚的问题。

使用世界上的示例代码,我尝试了以下方法,但无济于事。完成处理程序中的 Objective-C++ 代码永远不会被调用。它返回到 C++ 代码并且永远不会返回。

这是目标 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());
4

2 回答 2

4

如果我理解您的问题,您希望get(std::string)在完成提取后“给您回电”。如果您愿意更改接口,get那么一种方法是将 astd::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++ 的 newstd::promisestd::futuretypes ...

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,那么您仍然可以通过 usingboost::function或裸函数指针 -来使用第一种方法void(*)(std::string)。我对 OS X 的块功能也不太熟悉,所以要小心我的语法。

于 2015-02-02T23:32:45.437 回答
0

您似乎误解了如何dataTaskWithURL:completionHandler:工作;它是异步的(与大多数网络操作一样),这意味着任务将在单独的线程上运行,而不是阻塞调用线程。

这意味着您将在完成get(..)之前返回NSURLSessionDataTask

如果要进行同步网络调用,则必须阻塞或忙等待,直到returnVal填充。我已经修改了您的 obj-c 代码以使用dispatch_semaphore_ts 跟随此类的引导。

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);
于 2015-02-02T22:20:07.507 回答