I have a scenario in an iOS application where manipulating a very large NSString instance (an HTTP response, upwards of 11MB) results in multiple large intermediaries being in memory at once, since the SDK methods I am calling return new autoreleased instances. What is the best approach to take here?
For example, assuming that largeString is an autoreleased NSString instance:
NSArray *partsOfLargeString = [largeString componentsSeparatedByString:separator];
for (NSString *part in partsOfLargeString) {
NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
}
It would be great if there were non-autoreleased equivalents to componentsSeparatedByString or stringByTrimmingCharactersInSet, but I'm not looking to implement these myself.
To my knowledge, there isn't a way to "force" release an object that has already been added to an autorelease pool. I know that I can create and use my own autorelease pool here, but I'd like to be extremely granular and having autorelease pools around individual statements definitely isn't a very scalable approach.
Any suggestions are much appreciated.
As Bill said, I’d first try to have an autorelease pool for each loop iteration, e.g.:
for (NSString *part in partsOfLargeString) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
…
[pool drain];
}
or, if you’re using a recent enough compiler:
for (NSString *part in partsOfLargeString) {
@autoreleasepool {
NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
…
}
}
If that’s still not acceptable and you do need to release objects in a more granular fashion, you could use something like:
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);})
which evaluates the expression, retains its result, releases any ancillary autoreleased objects and returns the result; and then:
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];
}
Note that, since the function returns a retained object, you’re responsible for releasing it. You’ll have control over when to do that.
Feel free to choose better names for the function and macro. There might be some corner cases that should be handled but it should work for your particular example. Suggestions are welcome!