我正在尝试在应用程序中建立FTP连接。我想把几个文件上传到FTP服务器,所有文件都在一个目录中。所以一开始我想创建远程目录。
- (void) createRemoteDir {
NSURL *destinationDirURL = [NSURL URLWithString: uploadDir];
CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL);
assert(writeStreamRef != NULL);
ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef;
BOOL success = [ftpStream setProperty: ftpUser forKey: (id)kCFStreamPropertyFTPUserName];
if (success) {
NSLog(@"tsuccessfully set the user name");
}
success = [ftpStream setProperty: ftpPass forKey: (id)kCFStreamPropertyFTPPassword];
if (success) {
NSLog(@"tsuccessfully set the password");
}
ftpStream.delegate = self;
[ftpStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// open stream
[ftpStream open];
}
当使用以下调用在后台线程中执行时,此代码不起作用:
[self performSelectorInBackground: @selector(createRemoteDir) withObject: nil];
我的猜测是(后台线程)runloop没有运行?如果我在主线程内发送消息,上传就可以了:
[self createRemoteDir];
因为主线程的运行循环已经启动并正在运行。
但相当大的文件将被上传;所以我想把工作负载放在后台线程中。但是,我如何以及在哪里设置NSRunLoop,以便在后台线程中进行整个上载?关于NSRunLoops的苹果文档(尤其是如何在不使用定时器/输入源的情况下启动它们,如本例)对我没有帮助。
我找到/创建了一个至少适合我的解决方案。使用上述方法(createRemoteDir),应用了以下代码并对我有效:
NSError *error;
createdDirectory = FALSE;
/*
only 'prepares' the stream for upload
- doesn't actually upload anything until the runloop of this background thread is run
*/
[self createRemoteDir];
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
do {
if(![currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]) {
// log error if the runloop invocation failed
error = [[NSError alloc] initWithDomain: @"org.mJae.FTPUploadTrial"
code: 23
userInfo: nil];
}
} while (!createdDirectory && !error);
// close stream, remove from runloop
[ftpStream close];
[ftpStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
if (error) {
// handle error
}
它在后台线程中运行,并在ftp服务器上创建目录。我更喜欢它,而不是其他运行循环只运行一个假定的小间隔的例子,比如1秒。
[NSDate distantFuture]
是一个未来的日期(根据苹果公司的文件,几个世纪)。但这很好,因为"中断条件"由我的类属性createdDirectory处理,或者在启动运行循环时发生错误。
我无法解释为什么它在没有明确将输入源附加到运行循环(NSTimer或NSPort)的情况下工作,但我的猜测是,NSOutputStream在后台线程的运行循环中调度就足够了(请参见createRemoteDir)。
您也可以尝试使用dispatch_async调用在后台执行createRemoteDir。它的使用要简单得多,而且您不必担心管理额外的线程。
以下是代码的样子:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self createRemoteDir];
});