Ошибка POSIX 12 («Не удается выделить память») при загрузке файлов с iPhone - PullRequest
9 голосов
/ 28 декабря 2010

Я работаю над приложением для iPhone, которое включает в себя загрузку полных фотографий с камеры (обычно от 1,5 до 2,0 МБ каждая), а также их миниатюр (намного меньше) в Amazon S3.

Миниатюры всегда успешно загружаются, но иногда полные изображения не загружаются, а когда они терпят неудачу, они терпят неудачу с кодом ошибки POSIX 12, иначе ENOMEM. Тем не менее, я добавил отладочный код для печати объема свободной памяти при возникновении ошибки, и всегда есть немного свободного места, обычно более 100 МБ.

Кроме того, ошибка возникает чаще, когда загрузка происходит через 3G, и реже, когда она происходит через Wi-Fi - что кажется странным, так как запрос не загружает много, а загружаемый файл уже находится в памяти (я мы также попытались воспроизвести его с диска без каких-либо улучшений).

Я пытался загрузить файл, используя NSURLConnection, функции Foundation CFHTTP * и библиотеку ASIHTTPRequest, но независимо от того, ошибка происходит с той же частотой. Еще более странно, что все, что показал мой Google, - это то, что конечные пользователи иногда получают код ошибки 12 от Safari - я не видел ни одного разработчика iOS, упоминающего это. Я работаю с базой унаследованного кода, так что, возможно, с ней что-то не так, но я даже не уверен, что искать. Любая идея будет принята с благодарностью!

Ответы [ 3 ]

2 голосов
/ 30 июня 2011

Единственный способ обойти эту проблему - использовать сокеты напрямую и формировать HTTP-заголовок вручную.Таким образом, мой загружаемый код в настоящее время выглядит следующим образом:

- (void)socketClose
{
    [_inputStream setDelegate:nil];
    [_inputStream close];
    [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    SCR_RELEASE_SAFELY(_inputStream);

    [_outputStream setDelegate:nil];
    [_outputStream close];
    [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    SCR_RELEASE_SAFELY(_outputStream);

    SCR_RELEASE_SAFELY(_headerBuffer);
}

- (void)sendRequest
{
    [self socketClose];
    SCR_RELEASE_SAFELY(_headerBuffer);

    if (!_shouldCancel)
    {
        NSString *httpMessage = [NSString stringWithFormat:@"POST upload.php HTTP/1.1\r\n"
                                 "Host:"
#ifndef TESTBED
                                 " %@"
#endif
                                 "\r\n"
                                 "User-Agent: MyApp/3.0.0 CFNetwork/534 Darwin/10.7.0\r\n"
                                 "Content-Length: %d\r\n"
                                 "Accept: */*\r\n"
                                 "Accept-Language: en-us\r\n"
                                 "Accept-Encoding: gzip, deflate\r\n"
                                 "Content-Type: application/x-www-form-urlencoded\r\n"
                                 "Connection: keep-alive\r\n\r\n"
                                 "data="
#ifndef TESTBED
                                 , [self.serverUrl host]
#endif
                                 , _bytesToUpload];

        NSString *key = @"data=";
        NSData *keyData = [key dataUsingEncoding:NSASCIIStringEncoding];
        _bytesToUpload -= [keyData length];
        _bytesToUpload = MAX(0, _bytesToUpload);

        _headerBuffer = [[NSMutableData alloc] initWithData:[httpMessage dataUsingEncoding:NSUTF8StringEncoding]];

        _writtenDataBytes = 0;

        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault
                                           , (CFStringRef)[self.serverUrl host]
#ifdef TESTBED
                                           , 8888
#else
                                           , 80
#endif
                                           , (CFReadStreamRef *)(&_inputStream)
                                           , (CFWriteStreamRef *)(&_outputStream));

        [_inputStream setDelegate:self];
        [_outputStream setDelegate:self];

        [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

        [_inputStream open];
        [_outputStream open];
    }
}

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
{
    if (_outputStream == theStream)
    {
        switch (streamEvent)
        {
            case NSStreamEventOpenCompleted:
            {
                [self regenerateTimeoutTimer];
                break;
            }
            case NSStreamEventHasSpaceAvailable:
            {
                SCR_RELEASE_TIMER(_timeoutTimer);
                NSInteger length = _headerBuffer.length;

                if (length > 0)
                {
                    NSInteger written = [_outputStream write:(const uint8_t *)[_headerBuffer bytes] maxLength:length];
                    NSInteger rest = length - written;

                    if (rest > 0)
                    {
                        memmove([_headerBuffer mutableBytes], (const uint8_t *)[_headerBuffer mutableBytes] + written, rest);
                    }

                    [_headerBuffer setLength:rest];
                }
                else
                {
                    const uint8_t *dataBytes = [_data bytes];

                    while ([_outputStream hasSpaceAvailable] && (_writtenDataBytes < _bytesToUpload))
                    {
                        NSInteger written = [_outputStream write:dataBytes
                                                       maxLength:MIN(_dataLength, _bytesToUpload - _writtenDataBytes)];

                        if (written > 0)
                        {
                            _writtenDataBytes += written;
                        }
                    }
                }

                [self regenerateTimeoutTimer];

                break;
            }
            case NSStreamEventErrorOccurred:
            {
                SCR_RELEASE_TIMER(_timeoutTimer);
                [self reportError:[theStream streamError]];                
                break;
            }
            case NSStreamEventEndEncountered:
            {
                SCR_RELEASE_TIMER(_timeoutTimer);
                [self socketClose];
                break;
            }
        }
    }
    else if (_inputStream == theStream)
    {
        switch (streamEvent)
        {
            case NSStreamEventHasBytesAvailable:
            {
                SCR_RELEASE_TIMER(_timeoutTimer);

                /* Read server response here if you wish */

                [self socketClose];

                break;
            }
            case NSStreamEventErrorOccurred:
            {
                SCR_RELEASE_TIMER(_timeoutTimer);
                [self reportError:[theStream streamError]];
                break;
            }
            case NSStreamEventEndEncountered:
            {
                SCR_RELEASE_TIMER(_timeoutTimer);
                [self socketClose];
                break;
            }
        }
    }
}

Хотя ASIHTTPRequest мог бы работать здесь, мы решили отказаться от таких зависимостей как для повышения производительности, так и для точного хранения всего под нашим собственным контролем.Вы можете использовать инструмент Wireshark для отладки такого рода вещей.

1 голос
/ 10 февраля 2012

Устранена эта ошибка с использованием операции для запроса (NSMutableUrlConnection) с @autorelease{} для основной функцииNSPOXIS появляется только иногда.

- (void)main
 NSURLConnection* connection;
    @autoreleasepool //urgently needed for 3G upload
    {

        self.currentRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"test.php"]];
        [self.currentRequest setHTTPMethod:@"PUT"];

        [self.currentRequest setHTTPBody:self.data];//inpustStream doesn't work

        connection = [NSURLConnection connectionWithRequest:self.currentRequest delegate:self];
        [connection start];

    }//end autorelease pool

        do 
        {

            [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
            if ([self isCancelled])
            {
                connection          = nil;
                isFailed = YES;
                break;
            }
            self.status(statusUpdateMessage);
        } 
        while (!isFailed && !isCompleted);
        [timer invalidate];//test
        timer = nil;

//corresponding of status via blocks
        self.completed(!isFailed);
        self.status(isFailed ? errorMessage : @"Completed");
        if (isFailed)
        {
            self.failed(errorMessage != nil ? errorMessage : @"Undefined error");
        }

        self.data = nil;
        self.currentRequest = nil;

        connection = nil;

}
1 голос
/ 29 января 2011

Ключом к решению этой проблемы является загрузка файла с использованием потока. При использовании NSMutableURLRequest это может быть выполнено с помощью чего-то похожего на следующее:

NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:filePath]];

При использовании ASIHTTPRequest потоковая передача файла выполняется следующим образом:

ASIHTTPRequest* request = [ASIHTTPRequest requestWithURL:url];
[request setPostBodyFilePath:filePath];
rRequest.shouldStreamPostDataFromDisk = YES;
...