NemeSys
NemeSys

Reputation: 555

NSString from a large amount of items

I'm developing an app for iOS. This is a game.

In the game screen user can load object subclassed from UIImageView. To save the game I've developed this function that iterates by the subviews array of the main view and generates an XML string.

#pragma mark I/O Functions
-(NSString *)getXMLFromItemsView:(UIView *)items
{
    int a;
    NSString *XML = [[[NSString alloc] init] autorelease];

    XML = [XML stringByAppendingString:@"<PAGE>"];

    for (a=0;a<[items.subviews count];a++)
    {
        iItem *item = [items.subviews objectAtIndex:a];

        if ([item isKindOfClass:[iItem class]] || [item isKindOfClass:[iText class]])
        {
            NSLog(@"INFO -> Saving...Item Id: %d",item.itemId);

            XML = [XML stringByAppendingString:@"<ITEM>\n"];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<CLASS>%@</CLASS>\n",[item class]]];

            //OriginalTransform
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalTransform.a>%f</originalTransform.a>\n",item.originalTransform.a]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalTransform.b>%f</originalTransform.b>\n",item.originalTransform.b]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalTransform.c>%f</originalTransform.c>\n",item.originalTransform.c]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalTransform.d>%f</originalTransform.d>\n",item.originalTransform.d]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalTransform.tx>%f</originalTransform.tx>\n",item.originalTransform.tx]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalTransform.ty>%f</originalTransform.ty>\n",item.originalTransform.ty]];

            //InitialTransform
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<initialTransform.a>%f</initialTransform.a>\n",item.initialTransform.a]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<initialTransform.b>%f</initialTransform.b>\n",item.initialTransform.b]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<initialTransform.c>%f</initialTransform.c>\n",item.initialTransform.c]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<initialTransform.d>%f</initialTransform.d>\n",item.initialTransform.d]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<initialTransform.tx>%f</initialTransform.tx>\n",item.initialTransform.tx]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<initialTransform.ty>%f</initialTransform.ty>\n",item.initialTransform.ty]];

            //OriginalFrame
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalFrame.x>%f</originalFrame.x>\n",item.originalFrame.origin.x]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalFrame.y>%f</originalFrame.y>\n",item.originalFrame.origin.y]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalFrame.w>%f</originalFrame.w>\n",item.originalFrame.size.width]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<originalFrame.h>%f</originalFrame.h>\n",item.originalFrame.size.height]];

            //zOrder
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<zOrder>%d</zOrder>\n",item.zOrder]];

            //itemId
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<itemId>%d</itemId>\n",item.itemId]];

            //itemIdColor
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<itemIdColor>%d</itemIdColor>\n",item.itemIdColor]];

            //itemIdTexture
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<itemIdTexture>%d</itemIdTexture>\n",item.itemIdTexture]];

            //itemIdStyle
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<itemIdStyle>%d</itemIdStyle>\n",item.itemIdStyle]];

            //textureLevel
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<textureLevel>%d</textureLevel>\n",item.textureLevel]];

            //currentSizePercent
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentSizePercent>%f</currentSizePercent>\n",item.currentSizePercent]];

            //currentRotation
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentRotation>%f</currentRotation>\n",[item currentRotation]]];

            //holdItem
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<holdItem>%d</holdItem>\n",item.holdItem]];

            //isInverted
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<isInverted>%d</isInverted>\n",item.isInverted]];

            //isMerged
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<isMerged>%d</isMerged>\n",item.isMerged]];

            //selectedFrame
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<selectedFrame>%@</selectedFrame>\n",item.selectedFrame]];

            //mergedFrame
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<mergedFrame>%@</mergedFrame>\n",item.mergedFrame]];

            //NSLog(@"Item Saved Frame: X:%f Y:%f W:%f H:%f",item.frame.origin.x,item.frame.origin.y,item.frame.size.width,item.frame.size.height);

            //currentFrame
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentFrame.x>%f</currentFrame.x>\n",item.frame.origin.x]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentFrame.y>%f</currentFrame.y>\n",item.frame.origin.y]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentFrame.w>%f</currentFrame.w>\n",item.frame.size.width]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentFrame.h>%f</currentFrame.h>\n",item.frame.size.height]];

            //currentBounds
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentBounds.x>%f</currentBounds.x>\n",item.bounds.origin.x]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentBounds.y>%f</currentBounds.y>\n",item.bounds.origin.y]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentBounds.w>%f</currentBounds.w>\n",item.bounds.size.width]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentBounds.h>%f</currentBounds.h>\n",item.bounds.size.height]];

            //currentTransform
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentTransform.a>%f</currentTransform.a>\n",item.transform.a]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentTransform.b>%f</currentTransform.b>\n",item.transform.b]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentTransform.c>%f</currentTransform.c>\n",item.transform.c]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentTransform.d>%f</currentTransform.d>\n",item.transform.d]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentTransform.tx>%f</currentTransform.tx>\n",item.transform.tx]];
            XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentTransform.ty>%f</currentTransform.ty>\n",item.transform.ty]];

            if ([[NSString stringWithFormat:@"%@",[item class]] isEqual:@"iText"])
            {
                XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<currentText>%@</currentText>\n",[item text]]];

            }

            XML = [XML stringByAppendingString:@"</ITEM>\n"];

        }
    }

    if (fondo)
    {
        XML = [XML stringByAppendingString:@"<FONDO>\n"];

        XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<selectedFrame>%@</selectedFrame>",fondo.selectedFrame]];
        XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<picturePath>%@</picturePath>",fondo.picturePath]];

        XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<idCurrentColor>%d</idCurrentColor>",fondo.idCurrentColor]];
        XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<idCurrentTexture>%d</idCurrentTexture>",fondo.idCurrentTexture]];
        XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<idCurrentStyle>%d</idCurrentStyle>",fondo.idCurrentStyle]];
        XML = [XML stringByAppendingString:[NSString stringWithFormat:@"\t<textureLevel>%d</textureLevel>",fondo.textureLevel]];

        XML = [XML stringByAppendingString:@"</FONDO>\n"];
    }

    XML = [XML stringByAppendingString:@"</PAGE>"];
    NSLog(@"INFO -> ItemsViewXML: %@",XML);
    return XML;
}

At the moment this method works fine if the amount of items to iterate is low >20. But when it has to handle more than 50 items the app crashes because memory.

I've debugged this method using instruments and no leaks where detected, but the allocations increase until use a high amount of RAM.

It's not normal that creating a string uses more than 100MB of VM.

Anyone knows which I'm loosing?

Thanks.

Upvotes: 0

Views: 229

Answers (2)

danyowdee
danyowdee

Reputation: 4698

First, you are not creating a string, you are currently creating O(50) strings per object. This is highly inefficient — spatially (as you see in your memory usage) and temporally (it should take a shitload of time).

So you could go with Mattias˚ suggestion and use a mutable string to which you’d append data.

This will fix a lot of the memory-pressure-issue you are seeing, but will still be slow:
Parsing format strings is relatively time-consuming and a save operation should be as fast as possible.

So let’s just ignore the symptoms and look at the problem:

You basically want an archive of an object hierarchy, right?

In your custom classes, implement -initWithCoder: and -encodeWithCoder:.

Your save operation then becomes as simple as

NSData *encodedObjectGraph = [NSKeyedArchiver archivedDataWithRootObject:yourObjectGraphsRootGoesHere];
NSError *saveError;
if ([encodedObjectGraph writeToURL:storeURL options:NSDataWritingAtomic error:&saveError])
    return;

NSLog(@"Uh oh: %@", saveError);

and your load operation merely becomes

NSError *loadError;
NSData *savedObjectData = [NSData dataWithContentsOfURL:storeURL options:0 error:&loadError];
if (!savedObjectData) {
    NSLog(@"No data at %@: %@", storeURL, loadError);
    return;
}

objectGraphRoot = [NSKeyedUnarchiver unarchiveRootObjectWithData:savedObjectData];

(Note that unarchiveRootObjectWithData: returns an autoreleased object!)

Upvotes: 2

Mattias Wadman
Mattias Wadman

Reputation: 11425

You should use NSMutableString when building a string from parts.

NSMutableString *xml = [NSMutableString string];
[xml appendFormat:@"....", ...];
[xml appendFormat:@"....", ...];

But in this case you might even want to use a proper XML serializer like NSXMLDocument etc.

Upvotes: 1

Related Questions