基本任务

我有一些使用某些C++流接口的多平台库。我必须使用此流接口通过NSURLSession上传数据。我的实现应在OS X和iOS上运行(当前我正在OS X上进行测试)

我做了什么

任务看起来很简单,我确定我会很快实现这一点。
我已经配置了NSURLSession,如果我将NSURLRequest与简单的NSData一起使用,则可以正常工作。
我正在尝试使用像这样的流:

        NSURLSessionDataTask *dataTask = [m_Private.session uploadTaskWithStreamedRequest: request];
        HTTPDownoadTaskProxy *dataTaskProxy = [HTTPDownoadTaskProxy new];
        // store data to properly handle delegate
        dataTaskProxy.coreTask = dataTask;
        dataTaskProxy.cppRequest= req;
        dataTaskProxy.cppResponseHandler = handler;
        dataTaskProxy.cppErrorHandler = errorHandler;

        m_Private.streamedDataTasks[dataTask] = dataTaskProxy;

        [dataTask resume];

到现在为止还挺好。根据uploadTaskWithStreamedRequest的文档,我应该收到委托的通知,并且确实收到了它:
- (void)URLSession: (NSURLSession *)session
              task: (NSURLSessionTask *)task
 needNewBodyStream: (void (^)(NSInputStream *bodyStream))completionHandler
{
    HTTPDownoadTaskProxy *proxyTask = self.streamedDataTasks[task];
    CppInputStreamWrapper *objcInputStream = [[CppInputStreamWrapper alloc] initWithCppInputStream:proxyTask.cppRequest.GetDataStream()];
    completionHandler(objcInputStream);
}

现在,我应该在NSInputStream子类中接收调用,在我的情况下为CppInputStreamWrapper,这也很简单:
@implementation CppInputStreamWrapper

- (void)dealloc {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

- (instancetype)initWithCppInputStream: (const std::tr1::shared_ptr<IInputStream>&) cppInputStream
{
    if (self = [super init]) {
        _cppInputStream = cppInputStream;
    }
    return self;
}

#pragma mark - overrides for NSInputStream
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
    return (NSInteger)self.cppInputStream->Read(buffer, len);
}

- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len {
    return NO;
}

- (BOOL)hasBytesAvailable {
    return !self.cppInputStream->IsEOF();
}

#pragma mark - this methods are need to be overridden to make stream working
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

#pragma mark - Undocumented CFReadStream Bridged Methods
- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
                     forMode:(__unused CFStringRef)aMode
{}

- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
                         forMode:(__unused CFStringRef)aMode
{}

- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
                 callback:(__unused CFReadStreamClientCallBack)inCallback
                  context:(__unused CFStreamClientContext *)inContext {
    return NO;
}

@end

所以我在子类化NSInputStream时使用了所需的解决方法。

问题

现在这应该工作。但是我没有收到CppInputStreamWrapper方法的任何调用(除了我在构造对象时的调用)。

没有错误,没有警告,没有任何报告!

当我添加异常断点时,我正在捕捉
thread #8: tid = 0x155cb3, 0x00007fff8b770743 libobjc.A.dylib`objc_exception_throw, name = 'com.apple.NSURLConnectionLoader', stop reason = breakpoint 1.1

这来自我没有创建的com.apple.NSURLConnectionLoader线程。

我完全不知所措,不知道还能做什么。

更新资料

我已经使用了link in comment的代码形式hosted on github
现在,至少我的 class 的某些部分是由框架调用的,但是我看到了奇怪的崩溃。

崩溃位于此方法中:
- (BOOL)_setCFClientFlags:(CFOptionFlags)inFlags
                 callback:(CFReadStreamClientCallBack)inCallback
                  context:(CFStreamClientContext *)inContext {

    if (inCallback != NULL) {
        requestedEvents = inFlags;
        copiedCallback = inCallback;
        memcpy(&copiedContext, inContext, sizeof(CFStreamClientContext));

        if (copiedContext.info && copiedContext.retain) {
            copiedContext.retain(copiedContext.info);
        }

        copiedCallback((__bridge CFReadStreamRef)self, kCFStreamEventHasBytesAvailable, &copiedContext); // CRASH HERE
    } else {
        requestedEvents = kCFStreamEventNone;
        copiedCallback = NULL;
        if (copiedContext.info && copiedContext.release) {
            copiedContext.release(copiedContext.info);
        }

        memset(&copiedContext, 0, sizeof(CFStreamClientContext));
    }

    return YES;

}

崩溃是EXC_BAD_ACCESS(在OS X上运行测试时)。当我看到这段代码时,一切看起来都很好。它应该工作! self指向保留计数为3的正确对象,所以我不知道为什么它崩溃了。

最佳答案

未记录的 private 桥接API并不是自定义NSInputStream实现中的唯一问题,尤其是在CFNetworking集成的情况下。我想建议将POSInputStreamLibrary用作基本构建块。而不是实现许多NSInputStream方法和支持异步通知,您应该实现更简单的POSBlobInputStreamDataSource接口。至少您可以查看POSBlobInputStream来咨询应该实现什么样的功能以完全支持NSInputStream协定。

POSInputStreamLibrary用于最流行的俄罗斯云存储服务Cloud Mail.Ru,每天上传超过1M个文件,而不会发生任何崩溃。

祝你好运,随时问任何问题。

08-05 22:19