CFNetworkを使用したFTP接続のiOS6でのエラーに関して

ちょっと面倒なエラーが出たので備忘として記録しておきます。プロジェクトで使用している自作のFTPクラスでは、CFNetworkで作成したFTPストリームを、NSInputStreamに変換して、FTPの接続を行っています。メインキュー以外から呼ばれる前提で、非同期ではなく、同期的に呼ぶようにしています。

- (void) startGetSynchronous {
self.sema = dispatch_semaphore_create(0);
self.myFTPType = kFTPTypeGet;
CFReadStreamRef ftpStream;

NSInputStream * networkStream;

assert(networkStream == nil); // don't tap receive twice in a row!

ftpStream = CFReadStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) self.ftpURL);
assert(ftpStream != NULL);

networkStream = (__bridge_transfer NSInputStream *) ftpStream;
[networkStream setProperty:(id)kCFBooleanTrue forKey:(id)kCFStreamPropertyFTPUsePassiveMode];

networkStream.delegate = self;
dispatch_async(dispatch_get_main_queue(), ^{
[networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[networkStream open];
});

// Have to release ftpStream to balance out the create. self.networkStream
// has retained this for our persistent use.

self.error = nil;
self.dataReceived = [NSMutableData dataWithLength:0];

dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
#if OS_OBJECT_USE_OBJC!=1
dispatch_release(self.sema);
#endif
}

このサンプルは、Appleのsampleに基づいて作っていました。SimpleFTPSample: Document Revision History SimpleFTPSampleでも、依然はこのように、InputStream自体は、retainせずに、RunLoopに追加する事によってオブジェクトが生存する形となっていました。
これで、iOS5でも問題無く動いていました。ただiOS6で基本的に動くのですが、ときどきエラーが起きてFTPが接続出来なくなったりしていました。エラーログを見ると、

Error: request (0x______) other than the current request(0x0) signalled it was complete on connection 0x_______

のようなエラーが出ていました。このメッセージで検索してみると、別のFTPマネージャーでも同じようなエラーが出ている模様。
Error: request (0x989dd00) other than the current request(0x0) signalled it was complete on connection 0x9b8c6e0 · Issue #5 · nkreipke/FTPManager

ここでは、ARC環境においてのメモリ管理が関係しているのではないか、そしてオブジェクトをstrongプロパティにすればよいのではないかという議論が行われていたので、僕の個人のクラスも、NSStreamオブジェクトをクラスのプロパティに変更して見たところ、エラーが発生しないようになっていました。

self.networkStreamInput = CFBridgingRelease(ftpStream);
[self.networkStreamInput setProperty:(id)kCFBooleanTrue forKey:(id)kCFStreamPropertyFTPUsePassiveMode];

self.networkStreamInput.delegate = self;
dispatch_async(dispatch_get_main_queue(), ^{
[self.networkStreamInput scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.networkStreamInput open];
});

SimpleFTPSample自体も、2013/4/12に修正されています。その修正履歴には、SimpleFTPSample: Document Revision History

2013-04-12 Fixed a bug that caused an incompatibility with iOS 6 (r. 13171568), along with other minor changes including (r. 12478239).

となっていて、iOS 6との互換性問題のバグによって修正されたとありますね。最初からクラスのプロパティか、strongのインスタンス変数にしておけばよかったと思いましたが、サンプル通りに作っていたので、サンプルと同じ問題にぶつかっていました。ただ、CFNetworkによるFTPの動作がiOS5以前とiOS6で変更されたというのは事実のようですので、もし使っている方はご注意を。

ちなみに、参考にしたページには僕からもgitHubのissuesページに問題が解決した事を伝え、こちらのクラスもiOS6対応版に修正されたようです。まさに問題を共有して改善していくソーシャルコーディングですね。
Error: request (0x989dd00) other than the current request(0x0) signalled it was complete on connection 0x9b8c6e0 · Issue #5 · nkreipke/FTPManager