带上传流的NSURLSession - 子类化NSInputStream - com.apple.NSURLConnectionLoader异常

时间:2015-08-05 16:22:31

标签: ios objective-c macos nsurlsession nsinputstream

基本任务

我有一些使用一些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];

到目前为止一切顺利。根据{{​​1}}的文件,我应该收到代表的通知,我确实收到了:

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

我很困惑,不知道我还能做些什么。

更新

我使用的代码格式link in commenthosted on github。 现在至少我的类的某些部分是由框架调用的,但我看到了奇怪的崩溃。

崩溃位于此方法中:

com.apple.NSURLConnectionLoader

崩溃是- (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; } (在OS X上运行测试时)。当我看到这个代码一切都很好。它应该工作! EXC_BAD_ACCESS指向具有保留计数3的正确对象,因此我不知道它为什么会崩溃。

2 个答案:

答案 0 :(得分:1)

未记录的私有桥接API不是自定义NSInputStream实现中的唯一问题,尤其是在CFNetworking集成的上下文中。我想建议使用我的POSInputStreamLibrary作为基本构建块。您应该实现更简单的POSBlobInputStreamDataSource接口,而不是实现大量NSInputStream方法并支持异步通知。至少你可以查看POSBlobInputStream来查看你应该实现什么样的功能来完全支持NSInputStream合约。

POSInputStreamLibrary用于最受欢迎的俄罗斯云存储服务Cloud Mail.Ru,每天上传&gt; 1M文件,不会发生任何崩溃。

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

答案 1 :(得分:0)

我看到你确实有未记录的CFReadStream桥接方法的实现 - 这是更常见的问题之一。但是......请注意NSStream类的NSStream.h标头中的注释:

// NSStream is an abstract class encapsulating the common API to NSInputStream and NSOutputStream.
// Subclassers of NSInputStream and NSOutputStream must also implement these methods.

这意味着您还需要实现-open,-close,-propertyForKey:, - streamStatus等 - 基本上在NSStream和NSInputStream上声明的每个方法。尝试在你的代码中调用自己打开(NSURLConnection最终会这样做) - 你会得到这个想法,因为它应该在那里崩溃。您可能至少需要一些最小的状态处理,以便在调用-open之后-streamStatus不会返回NSStreamStatusNotOpen。基本上,每个具体的子类都需要实现整个API。它不像普通的类集群,只需要覆盖几个核心方法 - 甚至必须实现-delegate和-setDelegate:方法(超类没有实例变量存储,I&# 39;我很确定。)

AFNetworking有一个内部AFMultipartBodyStream,它具有所需的最小实现 - 你可以在AFURLRequestSerialization.m内看到这个例子。另一个代码示例是HSCountingInputStream