I've edited the previous post with some working code for convenience.
The follow code (ARC'ed) seems to leak and will crash the sim or the device after running for a short period of time:
#define kROWS 100
#define kCols 34
void run();
static ViewController *instance;
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *nsBackColor;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.nsBackColor = [NSMutableArray arrayWithCapacity:1];
instance = self;
// set up a '2D array'
for (int x = 0; x < kROWS; x++) {
[self.nsBackColor addObject:[NSMutableArray arrayWithCapacity:1]];
for (int y = 0; y < kCols; y++) {
[[self.nsBackColor objectAtIndex:x] addObject:[UIColor whiteColor]];
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
run();
});
}
- (void)plotColor:(UIColor *)color atX:(short)x andY:(short)y {
[[self.nsBackColor objectAtIndex:x] replaceObjectAtIndex:y withObject:color];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
void plot(short xLoc, short yLoc,
short backRed, short backGreen, short backBlue) {
#autoreleasepool {
[instance plotColor:[UIColor colorWithRed:((float)backRed/100)
green:((float)backGreen/100)
blue:((float)backBlue/100)
alpha:(float)1]
atX:xLoc andY:yLoc];
}
}
void run() {
short x = 0;
short y = 0;
short backRed = 0;
short backGreen = 0;
short backBlue = 0;
while (true) {
x++;
if (x >= kROWS) {
x = 0;
}
y++;
if (y >= kCols) {
y = 0;
}
backRed = arc4random() % 255;
backBlue = arc4random() % 255;
backGreen = arc4random() % 255;
plot(x, y, backRed, backGreen, backBlue);
usleep(1000);
}
}
If I let this run on the device or the simulator long enough (a couple minutes) I'll receive either an mmap malloc error (sim) or a memory warning (device) and crash.
Going through Instruments Allocations I can see +[UIColor colorWithRed:green:blue:alpha:] start to ballon until finally hitting the memory wall.
I can assign the UIColor to a property (directly or by doing a copy), say self.myColor = color, and there is no such leak.
I can do this too:
[[self.nsBackColor objectAtIndex:x] replaceObjectAtIndex:y withObject:[self description]];
and I get the same leak.
It seems to me that object replaced in the array (and yes, this did originally start as a 2D c array but I thought that was the issue) is forever lost and leaked and not properly released.
This would be the Instruments->Allocations after running for a short period of time:
Any help would be much appreciated and more information can be provided.
What is happening is that you are creating an autorelease pool at every iteration.
So the following line runs every time with a new autorelease pool:
[[self.nsBackColor objectAtIndex:x] replaceObjectAtIndex:y withObject:color];
Therefore color increases its reference count within the local pool by one, while [[self.nsBackColor objectAtIndex:x] objectAtIndex:y] decreases its reference count within the local pool by one. But here's the catch: That item was color in the previous iteration, which had its reference count managed by the previous pool that you drained/released earlier.
So what should have happened is as the previous pool was released in the previous iteration, that object was released. Its reference count was 2 (one for [UIColor colorWith...], one for adding to the array), so it should have received 2 release messages as soon as the pool was drained, and the pointer in [[self.nsBackColor objectAtIndex:x] objectAtIndex:y] should be left dangling until you replace it in the current iteration with a pointer to color.
Clearly that's not happening exactly the way it's supposed to be, or the way I understand it. However, the #autoreleasepool {} directive is clearly misplaced. It should be around the while (true) loop, or removed altogether in favor of the thread's pool.
Related
This problem is driving me crazy and I actually need some help.
I have to implement a particle filter in iOS and I started from a working code in Java.
The algorithm is very close to which is described in the course "Artificial Intelligence for Robotics" of Thrun on Udacity (https://www.udacity.com/course/artificial-intelligence-for-robotics--cs373)
The Java implementation has this behavior: I move the robot, the particles get closer and closer to the robot and then they follow it with a very low error. This is the expected behavior, I think. Changing number of particles, number of landmarks and noise, I get better or worse results.
The iOS implementation behavior is quite different: I move the robot, the particles get closer to the robot but after few iterations they start moving far from the robot. Initially the mean value is still close to the robot but then it jumps far.
Changing number of particles, number of landmarks and noise, I get to almost the same result.
I double checked the iOS code and apparently it's correct.
I suspected that the problem was related to random number generation and/or gaussian random generation.
I have changed my code to use exactly the code that Java library uses (http://docs.oracle.com/javase/7/docs/api/java/util/Random.html#nextGaussian()) getting the same results.
I moved the gaussian random generator to a singleton in order to share the same generator across all the particles, but nothing changed.
I have tried to change drand48() with ((double)arc4random() / (double)UINT32_MAX) getting the same results.
I have no other ideas.
I am not asking you to debug my code but please, give me any advice to sort this problem out.
EDIT 1
Maybe these pictures may help
From this step particles get far from the robot
EDIT 2
This is my Particle class:
#interface Particle ()
#property (nonatomic, strong) MyPoint *point;
#property double orientation;
#property NSInteger worldWidth;
#property NSInteger worldHeight;
#property double probability;
#property double forwardNoise;
#property double turnNoise;
#property double senseNoise;
#property (nonatomic,strong) Utils *utils;
#end
#implementation Particle
-(instancetype)initWithWorldWidth:(float)worldWidth worldHeight:(float)worldHeight {
self = [super init];
if (self) {
_worldWidth = worldWidth;
_worldHeight = worldHeight;
_point = [[MyPoint alloc] initWithX:drand48() * _worldWidth
Y:drand48() * _worldHeight];
_orientation = drand48() * 2. * M_PI;
_forwardNoise = 0;
_turnNoise = 0;
_senseNoise = 0;
_utils = [Utils sharedInstance];
}
return self;
}
-(void)setPosition:(MyPoint *)p orientation:(float)orientation probability:(double)probability {
_point.x = p.x;
_point.y = p.y;
_orientation = orientation;
_probability = probability;
}
-(void)setNoise:(double)forwardNoise turnNoise:(double)turnNoise senseNoise:(double)senseNoise {
_forwardNoise = forwardNoise;
_turnNoise = turnNoise;
_senseNoise = senseNoise;
}
-(MyPoint *)getPosition {
return _point;
}
-(double)getOrientation {
return _orientation;
}
-(double)getProbability {
return _probability;
}
-(double)getForwardNoise {
return _forwardNoise;
}
-(double)getTurnNoise {
return _turnNoise;
}
-(double)getSenseNoise {
return _senseNoise;
}
-(NSArray<NSNumber *>*)sense:(NSArray<Landmark*>*)landmarks {
NSMutableArray<NSNumber*> *measures = [[NSMutableArray alloc] init];
for(int i=0; i<landmarks.count; i++) {
Landmark *landmark = landmarks[i];
double distance = [Utils distanceBetweenP1:_point andP2:landmark];
double measure = distance + [_utils box_muller:0 :1.] * _senseNoise;
[measures addObject:[NSNumber numberWithDouble:measure]];
}
return measures;
}
-(void)moveForward:(double)forward withTurn:(double)turn {
//NSLog(#"---- Move ---- forward: %f ---- %f",forward,turn);
double a1 = [_utils box_muller:0. :1.];
//NSLog(#"\ta1=%.8f",a1);
_orientation = _orientation + turn + a1 * _turnNoise;
_orientation = [Utils circle:_orientation :2*M_PI];
double a2 = [_utils box_muller:0. :1.];
//NSLog(#"\ta2=%.8f",a2);
double dist = forward + a2 * _forwardNoise;
_point.x += cos(_orientation) * dist;
_point.y += sin(_orientation) * dist;
_point.x = [Utils circle:_point.x :_worldWidth];
_point.y = [Utils circle:_point.y :_worldHeight];
}
-(double)measurementProb:(NSArray<NSNumber *> *)measurements landmarks:(NSArray<Landmark *>*)landmarks {
double prob = 1.0;
for(int i=0; i<measurements.count; i++) {
Landmark *landmark = landmarks[i];
double measurement = [measurements[i] doubleValue];
double dist = [Utils distanceBetweenP1:_point andP2:landmark];
prob *= [Utils gaussian:dist :_senseNoise :measurement];
}
_probability = prob;
return prob;
}
This is my Particle filter:
#import "ParticleFilter.h"
#interface ParticleFilter ()
#property (nonatomic,strong) NSMutableArray<Particle *> *particles;
#property (nonatomic,strong) NSArray<Landmark *> *landmarks;
#property NSInteger worldWidth;
#property NSInteger worldHeight;
#end
#implementation ParticleFilter
-(instancetype)initWithLandmarks:(NSArray<Landmark*>*)landmarks numberOfParticles:(NSInteger)numberOfParticles worldWidth:(float)worldWidth worldHeight:(float)worldHeight {
self = [super init];
if (self) {
_worldWidth = worldWidth;
_worldHeight = worldHeight;
_particles = [[NSMutableArray alloc] init];
for (int i = 0; i < numberOfParticles; i++) {
[_particles addObject:[[Particle alloc] initWithWorldWidth:worldWidth worldHeight:worldHeight]];
}
_landmarks = [NSArray arrayWithArray:landmarks];
}
return self;
}
-(void)setNoise:(double)forwardNoise turnNoise:(double)turnNoise senseNoise:(double)senseNoise {
for (Particle *p in _particles) {
[p setNoise:forwardNoise turnNoise:turnNoise senseNoise:senseNoise];
}
}
-(void)moveForward:(double)forward withTurn:(double)turn {
for (Particle *p in _particles) {
[p moveForward:forward withTurn:turn];
}
}
-(void)resample:(NSArray<NSNumber *>*)measurements {
NSMutableArray<Particle *> *newParticles = [[NSMutableArray alloc] init];
for (Particle *p in _particles) {
[p measurementProb:measurements landmarks:_landmarks];
}
double B = 0;
Particle *bestParticle = [self getBestParticle];
NSInteger index = drand48() * _particles.count;
for (int i = 0; i < _particles.count; i++) {
B += drand48() * 2. * [bestParticle getProbability];
while (B > [_particles[index] getProbability]) {
B -= [_particles[index] getProbability];
index = [self circle:index+1 :_particles.count];
}
[newParticles addObject:_particles[index]];
}
[_particles removeAllObjects];
[_particles addObjectsFromArray:newParticles];
}
-(NSInteger)circle:(NSInteger) num :(NSInteger)length {
while(num > length - 1)
num -= length;
while(num < 0)
num += length;
return num;
}
-(Particle *)getAverageParticle {
Particle *p = [[Particle alloc] initWithWorldWidth:_worldWidth worldHeight:_worldHeight];
double x = 0;
double y = 0;
double orient = 0;
double prob = 0;
for(int i=0; i<_particles.count; i++) {
x += [_particles[i] getPosition].x;
y += [_particles[i] getPosition].y;
orient += [_particles[i] getOrientation];
prob += [_particles[i] getProbability];
}
x /= _particles.count;
y /= _particles.count;
orient /= _particles.count;
prob /= _particles.count;
[p setPosition:[[MyPoint alloc] initWithX:x Y:y]
orientation:orient
probability:prob];
[p setNoise:[_particles[0] getForwardNoise]
turnNoise:[_particles[0] getTurnNoise]
senseNoise:[_particles[0] getSenseNoise]];
return p;
}
Each movement is:
[_robot moveForward:2. withTurn:0];
[_particleFilter moveForward:2. withTurn:0];
NSLog(#"%#",_particleFilter);
NSLog(#"Mean %#",[_particleFilter getAverageParticle]);
NSArray<NSNumber*> *measurements = [_robot sense:_landmarks];
[_particleFilter resample:measurements];
NSLog(#"%#",_particleFilter);
NSLog(#"Mean %#",[_particleFilter getAverageParticle]);
NSLog(#"Robot %#",_robot);
NSLog(#"Estimated Robot %#",[_particleFilter getAverageParticle]);
NSLog(#"Best Robot %#",[_particleFilter getBestParticle]);
Here the code involving random numbers
#import "Utils.h"
#interface Utils ()
#property BOOL haveNextNextGaussian;
#property double nextNextGaussian;
#end
#implementation Utils
+ (instancetype) sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super alloc] initInstance];
});
return sharedInstance;
}
-(instancetype)initInstance {
self = [super init];
if (self) {
srand48(arc4random());
}
return self;
}
+(double)distanceBetweenP1:(MyPoint *)p1 andP2:(MyPoint *)p2 {
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
+(double)gaussian:(double)mu :(double)sigma :(double)x {
return exp(-(pow(mu - x, 2.)) / pow(sigma, 2.) / 2.0) / sqrt(2.0 * M_PI * pow(sigma, 2.));
}
-(double)box_muller:(double)m :(double)s {
if (_haveNextNextGaussian) {
_haveNextNextGaussian = NO;
return _nextNextGaussian;
} else {
double v1, v2, s;
do {
v1 = 2 * drand48() - 1; // between -1.0 and 1.0
v2 = 2 * drand48() - 1; // between -1.0 and 1.0
s = v1 * v1 + v2 * v2;
}
while (s >= 1 || s == 0);
double multiplier = sqrt(-2 * log(s)/s);
_nextNextGaussian = v2 * multiplier;
_haveNextNextGaussian = YES;
return v1 * multiplier;
}
}
+(double)circle:(double) num :(double)length {
while(num > length - 1)
num -= length;
while(num < 0)
num += length;
return num;
}
#end
This answer was posted as a "how do I find the problem with x" before the OP posted any code.
With code to look at, this may no longer be relevant, particularly since OP asserts that all steps listed below have been followed.
This is a vague answer, but it's a vague question..
It's not possible for us to give any advice or guidance here since the only piece of information really available to the reader is that you suspect that the problem lies in the random number generation.
This yor folt?
To answer this question as a reader, we're basically comparing the chances of a stranger (YOU) on the internet having made a mistake VS the chances of a mistake being made by a team of ios developers, and having that mistake go unnoticed. Unfortunately we have to put our bets on the mistake being your fault.
PROGRAMMER'S RULE OF THUMB: Test your code before blaming someone else's code. If you know the person you are about to blame, test it again. If the person you are about to blame sees you every day, test it a third time. If you are within punching distance of the person you are about to blame, test it once more.
Problem Solving (AKA How to Find and Fix Your Pooch Up)
The first step in solving any coding problem is identifying the problem.
Rather than haphazardly looking at your code for errors again and again (StackOverflow Users Agree(c) you will never find it):
Create some unit tests to verify your code expectations and make sure that you're doing things right. If you are, then..
If the units of code that you've tested are all behaving correctly, check that they behave correctly when combined. Create tests that check chains of operations. The longer the chains, the more of the end to end functionality of your app you will be testing. Still not working? You may consider...
Remote debug your application while it is running to see exactly what is going on. Can't get remote debug to work? You're making this hard, but...
Log a small set of data from your app. Create a test to run your data set through your code. Verify expectations. You still can't get it to work? Then try...
Post your question on StackOverflow, with relevant code and data set.
Basically TEST, TEST, TEST
Every test made will save you time in the long run if your app is in active development (very mature and stable code will benefit less).
I have an image processing application that I am using to test potential speedup of blocks/threads on iOS. The algorithm works fine, uses a fair amount of memory and then dumps it after running.
I made a test suite that runs the algorithm 10 times sequentially and 10 times using my parallel implementation. If I try to run it on a phone it crashes due to memory pressure - it ends up needing about 432MB of memory. However, once the suite is done it all finally gets cleaned up :/
Each individual run is using around 25MB of memory. So I thought the solution would be to reset all of my objects after each run and they would get cleaned up. I basically have 2 processing objects that do all of my work. So after each run I thought setting them to nil would cause them to be recreated and the old versions to be destroyed and that memory freed. However, it had no effect on my memory usage.
Is there something more I need to do to free up the memory in-between calls? I thought Objective-C was now using reference counting, and once I eliminate the only reference - in my viewController - that it would be freed. Any suggestions would be greatly appreciated.
This is the test suite algorithm with my attempted memory-freeing:
- (void)runTestSuite
{
// Sequential
NSLog(#"Sequential Run: ");
for (int i = 0; i < 10; i++) {
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:NO];
}
[self printResults];
[self clearResults];
// Parallel
NSLog(#"Parallel Run: ");
for (int i = 0; i < 10; i++) {
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:YES];
}
[self printResults];
[self clearResults];
}
Other than working on the algorithm to improve memory usage you could give #autoreleasepool a shot. This will free your freeable objects used in between every loop, without the need of the current loop cycle to end.
for (int i = 0; i < 10; i++) {
#autoreleasepool {
}
}
From the documentation:
In many situations, allowing temporary objects to accumulate until the
end of the current event-loop iteration does not result in excessive
overhead; in some situations, however, you may create a large number
of temporary objects that add substantially to memory footprint and
that you want to dispose of more quickly. In these latter cases, you
can create your own autorelease pool block. At the end of the block,
the temporary objects are released, which typically results in their
deallocation thereby reducing the program’s memory footprint
Some source code would help. In absence of that, a general suggestion: wrap the code that is doing the image process in an autoreleasepool (see this Apple document).
This will discard temporary objects as soon as possible, reducing memory spike.
Autorelease pool should be used here
Many programs create temporary objects that are autoreleased. These
objects add to the program’s memory footprint until the end of the
block. In many situations, allowing temporary objects to accumulate
until the end of the current event-loop iteration does not result in
excessive overhead; in some situations, however, you may create a
large number of temporary objects that add substantially to memory
footprint and that you want to dispose of more quickly. In these
latter cases, you can create your own autorelease pool block. At the
end of the block, the temporary objects are released, which typically
results in their deallocation thereby reducing the program’s memory
footprint
- (void)runTestSuite
{
// Sequential
NSLog(#"Sequential Run: ");
for (int i = 0; i < 10; i++) {
#autoreleasepool
{
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:NO];
}
}
[self printResults];
[self clearResults];
// Parallel
NSLog(#"Parallel Run: ");
for (int i = 0; i < 10; i++) {
#autoreleasepool
{
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:YES];
}
}
[self printResults];
[self clearResults];
}
Somehow I am seeing memory increase in my simulator (the running bar graph).
I have an object called MyObject which has nothing but an int. In some other class (say MyCar in MyCar.h) I have an array of MyObjects, e.g. MyObject* junk[8];
and a method to return a specific one by index:
-(MyObject*) getJunk:(int) index{
if(junk[index] == nil){
return nil;
}
return junk[index];
}
In another class called DataModel I have a MyCar *mycar;
in DataModel I call a method ask
-(void) ask{
if(mycar == nil){
return nil;
}
for(int i=0;i<8;i++){
if( [mycar getJunk:i] == nil){
continue;
}
}
}
I call [self ask] say 10,000 times each time. I am using ARC, but I notice each time the iOS memory bar goes up. So something is accumulating memory; I don't know what, I don't have any allocs. Yes, this happens even if I write such non useless code which does nothing with the junk[i] returned to data model.
I even tried
-(void) ask{
if(mycar == nil){
return nil;
}
for(int i=0;i<8;i++){
MyObject* temp_junk = [mycar getJunk:i];
if( temp_junk == nil){
continue;
}
temp_junk = nil;
}
}
If you are calling [self ask] 10000 times in a loop, like this:
for (int = 0; i < 10000; i++)
[self ask];
then you are accumulating autoreleased objects inside the loop - those objects will not be released until your loop ends and the main run loop flushes the autorelease pool. Try creating your own autorelease pool inside your loop, like this:
for (int i = 0 ; i < 10000; i++)
{
#autoreleasepool
{
[self ask];
}
}
That will create a new pool each time through the loop and flush it each time so those autoreleased objects will go away.
I've probably been starting at this too long and simply can't see the logic problem. I'm changing background images on a pan gesture. Swiping left/right cycles thru an array of image names for the background and loops.
Swiping Right (increasing) works fine, it loops back to the start of my array.
Swiping Left (decreasing stops at the first object in my array (objectIndex: 0).
NSLog(#"_imageBackgroundIndex Before:%d",_imageBackgroundIndex);
if ([_panDirection isEqual:#"Right"])
{
_imageBackgroundIndex = _imageBackgroundIndex + 1;
}
if ([_panDirection isEqual:#"Left"])
{
_imageBackgroundIndex = _imageBackgroundIndex - 1;
}
NSLog(#"_imageBackgroundIndex After:%d",_imageBackgroundIndex);
if (_imageBackgroundIndex > ([_backgroundImages count] - 1))
{
_imageBackgroundIndex = 0;
}
if (_imageBackgroundIndex < 0)
{
_imageBackgroundIndex = ([_backgroundImages count] - 1);
}
[[self childNodeWithName:kBackgroundName] runAction:
[SKAction setTexture:
[SKTexture textureWithImageNamed:
[_backgroundImages objectAtIndex:_imageBackgroundIndex]]]];
Anyone see the issue?
Code looks okay (though there are some style improvements to consider once you get it working). I'd wager that you have _imageBackgroundIndex declared as an unsigned int or NSUInteger. When you think it drops below zero, it's really taking on a huge unsigned value.
This code will work (and has better style) even if the counter is declared unsigned...
- (void)incrementIndex {
BOOL increment = self.imageBackgroundIndex < self.backgroundImages.count-1;
self.imageBackgroundIndex = (increment)? self.imageBackgroundIndex+1 : 0;
}
- (void)decrementIndex {
BOOL decrement = self.imageBackgroundIndex > 0;
self.imageBackgroundIndex = (decrement)? self.imageBackgroundIndex-1 : self.backgroundImages.count-1;
}
Then your pan method becomes simpler:
// not sure how panDirection is initialized, is it really a string #"Left" or #"Right"?
// if so, use isEqualToString to compare strings
if ([_panDirection isEqualToString:#"Right"]) [self incrementIndex];
else if ([_panDirection isEqualToString:#"Left"]) [self decrementIndex];
[[self childNodeWithName:kBackgroundName] runAction:// ... etc
I need to do this : alloc objects until memory warning is called and then release all objects. But I have some problems. How can I do this? I need code example because the problem is that : the code. I have a class that doesn't use ARC. This class has a method which alloc N objects that are saved into an array. I need the memory is filled until didReceiveMemoryWarning is called because this is the only method to "free" RAM memory on iOS. Then, I will release all. I think the cleaner memory apps for the iPhone on the App Store use this trick to "free" the memory.
Thanks in advance
You'll have to fill in the missing details but here is what I have used before. Credit goes to who/where ever I found it. This will work on ARC and non ARC projects. I have found that usually you get 2-3 warnings before you're completely dead. Good luck. Dinner length is how much of a chunk that gets allocated each time. if you want more fine grained memory control change the size.
-(IBAction)startEatingMemory:(id)sender
{
if(self.belly == nil){
self.belly = [NSMutableArray array];
}
self.paused = false;
[self eatMemory];
}
- (IBAction)pauseEat:(id)sender {
self.paused = true;
[[self class]cancelPreviousPerformRequestsWithTarget:self selector:#selector(eatMemory) object:nil];
}
- (IBAction)stopEatingMemory:(id)sender {
[self pauseEat:self];
[self.belly removeAllObjects];
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:#selector(eatMemory) object:nil];
}
-(void)eatMemory
{
unsigned long dinnerLength = 1024 * 1024;
char *dinner = malloc(sizeof(char) * dinnerLength);
for (int i=0; i < dinnerLength; i++)
{
//write to each byte ensure that the memory pages are actually allocated
dinner[i] = '0';
}
NSData *plate = [NSData dataWithBytesNoCopy:dinner length:dinnerLength freeWhenDone:YES];
[self.belly addObject:plate];
[self performSelector:#selector(eatMemory) withObject:nil afterDelay:.1];
}
-(void)didReceiveMemoryWarning
{
[self pauseEat:self];
<#Could release all here#>
[super didReceiveMemoryWarning];
}
I would edit/subclass the class that doesn't use ARC to either use ARC, or add a method to it that releases the N objects.