简体   繁体   中英

Holding pixel data in NSMutableArray/NSArray; memory issues

I'm trying to hold basic information about a pixel inside an object. Basically, (1) whether the pixel is clear and (2) whether the pixel is on what I am defining to be a surface edge. I'm using the following class to define the object:

@interface PixelInfo : NSObject
@property (nonatomic, assign) BOOL isClear;
@property (nonatomic, assign) BOOL isEdge;
@end

However, I'm running into low memory issues. I'm using this object to keep track of the state of a pixel in a game that has a destructible environment. The problem may be that for an iPad retina sized image (2048 x 1536 = 3 million pixels), I am creating 3 million of these objects and seems to be allocating hundreds of MB of memory, and is causing an iPhone device to force quit due to low memory issues.

Looking at the logs inside the iPhone device, I'm getting the following information:

Name     rpages     recent_max     [reason]             (state)       
MyApp    166400     166400         [per-process-limit]  (frontmost) (resume)

Using Instruments Allocations tool, I see hundreds of MB of memory being allocated to PixelInfo objects over time, until eventually, the iPhone device force quits.

Based on this, I'm guessing maybe I should not be holding pixel information like this. I'm also suspicious that Instruments Allocations is showing that the device is needing hundreds of MB of memory to accomplish this. Something may be going wrong elsewhere, but I can't seem to pinpoint it.

I do feel the need to keep track of the state of all the pixels. I'm using this pixel information to track the state of the image when I am destroying the environment (ie, setting pixel info isClear property to YES, and isEdge property to NO), then to recalculate the new edges for the affected portion of the environment.

Here are my main questions:

  1. Is it bad that I am attempting to hold 3 million objects inside an array?
  2. Low memory is clearly a problem (seen using the Instruments Allocations tool), but is it correct that hundreds of MB of memory needs to be used by these 3 million objects or is it likely that something else is causing issues?

Any thoughts or pointers on how I should debug this situation further would be really helpful. Thanks in advance.

-- Just for additional context (if relevant), this is how I am reading and storing pixel information from a texture (using some cocos2d classes):

unsigned char data[4];

// create texture to read pixel data from
CCRenderTexture *renderTexture = [[CCRenderTexture alloc] initWithWidth:widthInPoints
                                                                 height:heightInPoints
                                                            pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[renderTexture beginWithClear:1 g:1 b:1 a:0];

// self.view is a CCSprite object and the visit method draws it to the texture so I can read the pixels
[self.view visit];

for (int i = 0; i < heightInPixels; ++i) {
    for (int j = 0; j < widthInPixels; ++j) {

        // read pixel data
        CGPoint pixelPoint = ccp(j, i);
        glReadPixels(pixelPoint.x, pixelPoint.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data);

        // store pixel data in PixelInfo object; read alpha value in data
        PixelInfo *pixelInfo = [[PixelInfo alloc] init];
        pixelInfo.isClear = [self _isPixelClear:data];

        // add object to _pixelData array (NSMutableArray object)
        [_pixelData addObject:pixelInfo];
        [pixelInfo release];            
    }
}

// release texture
[renderTexture end];
[renderTexture release];

为了减少您的内存使用量,请使用两个布尔数组,而不要使用包含两个布尔对象的实例的数组

The problem is not that you're storing 3 million PixelInfo objects inside an NSArray. (3 million * (4 + 1 + 1) bytes, assuming 4 bytes for NSObject.isa and 1 byte each for BOOL is only 18 MB).

At least from the code you've posted, it appears that the problem is that you only add PixelInfo objects to the NSArray, but never release them. If you add 3 million objects into the array for each rendered frame, at 30fps, you could end up with 540MB (18MB * 30) of memory allocated per second. You can see how this could very quickly lead to the memory issues you're seeing.

Without knowing too many details, one suggestion for improvement is to create a single array of shorts and update the appropriate values in this array, instead of continuously creating new objects and storing them.

To illustrate:

short *pixelInfo = (short *) calloc(1, sizeof(short) * heightInPixels * widthInPixels); // use calloc to zero-fill the bytes
...
pixelInfo[i*heightInPixels+j] |= [self _isPixelClear: data]; // set isClear
pixelInfo[i*heightInPixels+j] |= [self _isPixelEdge: data] << 1; // set isEdge
pixelInfo[i*heightInPixels+j] & 0x1; // isClear set?
(pixelInfo[i*heightInPixels+j] >> 1) & 0x1; // isEdge set?

Of course, with this approach, you'll need to clear the pixel-info's between frames if this info does not persist across frames.

If you can articulate your needs in a little more detail, more specific/better suggestions could come up.

Use a bit array. One such implementation by Michael Dipperstein can be found here . The C version is included in Kobold2D and KoboldTouch .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM