【发布时间】:2015-08-05 16:22:30
【问题描述】:
基本任务
我有一些使用 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。 现在框架至少调用了我的类的某些部分,但我看到了奇怪的崩溃。
崩溃位于此方法中:
- (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,所以我不知道它为什么会崩溃。
【问题讨论】:
-
子类化 NSStreams 很有趣。声明的超类基本上不提供实际功能;你需要实现这一切。 Apple 的私有子类具有您无法重复使用的功能。更有趣的是,有些地方(NSURLConnection)假设你使用的是 Apple 的类,并调用私有方法。有关正在发生的事情的提示,请参阅 blog.bjhomer.com/2011/04/subclassing-nsinputstream.html。
标签: ios objective-c macos nsurlsession nsinputstream