iOS: Как избежать автоматически выпущенных копий при манипулировании большим экземпляром NSString? - PullRequest
7 голосов
/ 13 сентября 2011

У меня есть сценарий в приложении iOS, где манипулирование очень большим экземпляром NSString (ответ HTTP, объемом более 11 МБ) приводит к тому, что несколько крупных посредников находятся в памяти одновременно, поскольку вызываемые мной методы SDK возвращают новые автоматически выпущенные экземпляры.Какой наилучший подход использовать здесь?

Например, если предположить, что largeString является автоматически выпущенным экземпляром NSString:

NSArray *partsOfLargeString = [largeString componentsSeparatedByString:separator];

for (NSString *part in partsOfLargeString) {
    NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

    NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
}

Было бы замечательно, если бы не было автоматически выпущеноэквивалентен componentsSeparatedByString или stringByTrimmingCharactersInSet, но я не собираюсь сам реализовывать их.

Насколько мне известно, нет способа "принудительно" освободить объект, который уже был добавлен вАвто-релиз бассейн.Я знаю, что могу создавать и использовать свой собственный пул авто-релизов, но я хотел бы быть очень детальным, и иметь пулы авто-релизов вокруг отдельных операторов определенно не очень масштабируемый подход.

Любые предложения приветствуются.

Ответы [ 2 ]

2 голосов
/ 13 сентября 2011

Как сказал Билл, сначала я бы попытался создать пул авто-релиза для каждой итерации цикла, например:

for (NSString *part in partsOfLargeString) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
    …

    [pool drain];
}

или, если вы используете достаточно свежий компилятор:

for (NSString *part in partsOfLargeString) {
    @autoreleasepool {
        NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
        …
    }
}

Если это по-прежнему неприемлемо и вам нужно выпускать объекты более детально, вы можете использовать что-то вроде:

static inline __attribute__((ns_returns_retained))
id BICreateDrainedPoolObject(id (^expression)(void)) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];
    id object = expression();
    [object retain];
    [pool drain];
    return object;
}

#define BIOBJ(expression) BICreateDrainedPoolObject(^{return (expression);})

, которое оценивает выражение, сохраняет его результат, освобождает все вспомогательные автоматически выпущенныевозражает и возвращает результат;а затем:

for (NSString *part in partsOfLargeString) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSString *trimmedPart = BIOBJ([part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]);
    NSData *data = BIOBJ([trimmedPart dataUsingEncoding:NSUTF8StringEncoding]);
    [trimmedPart release];

    // do something with data
    [data release];

    …

    [pool drain];
}

Обратите внимание, что, поскольку функция возвращает сохраненный объект, вы отвечаете за его освобождение.Вы будете контролировать, когда это делать.

Не стесняйтесь выбирать лучшие имена для функции и макроса.Там могут быть некоторые угловые случаи, которые должны быть обработаны, но это должно работать для вашего конкретного примера.Предложения приветствуются!

1 голос
/ 13 сентября 2011

Во-первых, вам не нужно разбирать ответы с HTTP-сервера таким образом. Разбор HTTP-ответов (включая разбор HTML) - это решенная проблема, и попытка разобрать его с помощью необработанных манипуляций со строками приведет к хрупкому коду, который может быть легко сбит при казалось бы безобидных изменениях на стороне сервера.

Авто-релизные бассейны довольно дешевы. Вы можете окружить тело [внутри] for с помощью @autoreleasepool {... that code ...}, и это, вероятно, решит проблему с высокой водой и окажет незначительное влияние на производительность [по сравнению с необработанными манипуляциями с строками].

Кроме того, ваша сводка верна - если в наборе нет варианта без авто-выпуска, вам придется заново изобрести колесо. С учетом вышесказанного, довольно типично, что отсутствие варианта без автоматического освобождения не является упущением со стороны дизайнера. Вместо этого, скорее всего, потому, что есть лучшие инструменты для достижения решения большого объема, которое также потребовало бы более тонкого управления памятью.

...