загружать файлы в фоновом режиме через ftp на iphone

я пытаюсь установить 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];

Я предполагаю, что цикл выполнения (фоновых потоков) не работает? Если я отправлю сообщение внутри основного потока, загрузка будет работать нормально:

[self createRemoteDir];

поскольку цикл выполнения основного потока запущен и работает.

но будут загружены довольно большие файлы; поэтому я хочу поместить эту рабочую нагрузку в фоновый поток. но как и где мне настроить NSRunLoop, чтобы вся загрузка происходила в фоновом потоке? Документация Apple по NSRunLoops (особенно как их запускать без использования таймера/источника ввода, как в этом случае) мне не помогла.


person m Jae    schedule 05.02.2012    source источник
comment
я нашел аналогичный вопрос здесь; я действительно хотел бы увидеть код, используемый для его решения: ссылка   -  person m Jae    schedule 05.02.2012


Ответы (2)


Я нашел / создал решение, которое, по крайней мере, работает для меня. с указанным выше методом (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]

дата в будущем (несколько столетий, согласно документации Apple). Но это хорошо, поскольку «условие останова» обрабатывается свойством моего класса createdDirectory или возникновением ошибки при запуске цикла выполнения.

Я не могу объяснить, почему это работает без явного присоединения источника ввода к циклу выполнения (NSTimer или NSPort), но я предполагаю, что достаточно, чтобы NSOutputStream был запланирован в цикле выполнения фонового потока (см. createRemoteDir ).

person m Jae    schedule 06.02.2012
comment
Очень странно, что это работает, но, как вы говорите, ожидаемое решение не работает. - person SomaMan; 17.07.2020

Вы также можете попробовать использовать вызов dispatch_async для выполнения вашего createRemoteDir в фоновом режиме. Его намного проще использовать, и вам не придется беспокоиться об управлении дополнительными потоками.

Вот как будет выглядеть код:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self createRemoteDir];
});
person Simon Germain    schedule 05.02.2012
comment
не работает, потому что у него та же проблема, что и у моего подхода: как мне настроить NSRunLoop, который каким-то образом создан для этого потока gcd? мне это все еще нужно (см. предпоследнюю строку моего кода: [ftpStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];). разница только в создании фонового (рабочего) потока - person m Jae; 06.02.2012
comment
Вам не нужно настраивать NSRunLoop. GCD сделает все за вас. Не нужно беспокоиться об этом. - person Simon Germain; 06.02.2012
comment
я попробовал это. и для меня это было неудачно: gcd не настроил цикл выполнения. точно так же, как PerformSelectorInBackground:withObject не делал. - person m Jae; 06.02.2012