memory allocation in custom initialization - ios

Ran allocation profiler on my code. Found following problem on the inserted code. Could someone please point me what is wrong in the code. Added the picture so as too show the color coding.
Code for initializing the point is as below:
#autoreleasepool {
if(!coordString){
return nil;
}
if([coordString length]<3){
return nil;
}
__weak NSArray* coords=[coordString componentsSeparatedByString:#","];
if(nil != coords && ([coords count]==2)){
self = [super init];
if(nil != self){
self.coordX= [[coords objectAtIndex:0] doubleValue];
self.coordY = [[coords objectAtIndex:1] doubleValue];
return self;
}else {
return nil;
}
}else{
return nil;
}
}
Please suggest where the problem might be.
Allocation snapshot indicates the persistance memory.

There is no straight and clear solution one should expect on the memory related issues such as raised by my question above as there is no clear error is committed in the code. Leaks not picking anything specific. Hence #Avi above gave me a hint that the "Persistent" indicates the abandoned allocations. So his comment above gave me a direction that I need to look into the problems elsewhere.
Someone who hasn't watched, should spend time understanding handling of memory issues using Instrument on the link https://developer.apple.com/videos/play/wwdc2012-242/
More important is to create the "dealloc" function for you such classes and create deallocation code for your code and set the pointers to nil. In my case above I made sure to do that using following code:
-(void) dealloc{
if(nil != self.arrVertices && [self isKindOfClass:[SPCPlane2D class]]){
[self.arrVertices removeAllObjects];
}
self.arrVertices=nil;
}
With few hours of digging further I have got the instrument showing as below

Related

Is it more memory efficient to define and initialize an NSArray when a UIView first loads or only when a condition is met?

Is it better to initialize an NSArray in viewDidLoad when the program first starts up, or define the NSArray only if a condition is met?
Basically, I initialized an NSArray in an IF condition of one of my methods. This method may be called multiple times, and want to know if it's better on memory if the NSArray is created and destroyed in the method, or if it's better to define it once in viewDidLoad and reference it in the method?
If I'm not clear, please let me know.
Thanks
Create any data collection only when you need it, this way you make sure that the program/screen is initially launched quickly. This is called as lazy loading, this approach should be followed whenever possible.
If you are using NSMutableArray and managing this in run time, you can do something like this for lazy loading, and clearing it from memory when not needed. Add helper methods to add and remove objects from an array, array is created automatically when needed and removed from memory when it is empty.
- (void)addObject:(NSObject *)value
{
if (value == nil) return;
if (_collection == nil) {
_collection = [[NSMutableArray alloc] init];
}
[_collection addObject:value];
}
- (void)removeObject:(NSObject *)value
{
if (value == nil) return;
[_collection removeObject:value];
if ([_collection count] == 0) {
[_collection release], _collection = nil;
}
}

Memory Leak in MapKit iOS8

I've read the other StackOverflow Questions and Answers, and understand this is a bug since iOS6 (or by design, having to deallocate the delegate, then view, who knows). I don't know why or how it hasn't been fixed.
Anywho, I've added the hot fixes from the other answers (below, for future readers):
- (void) viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self applyMapViewMemoryHotFixOnDisappear];
}
- (void)applyMapViewMemoryHotFixOnDisappear{
[self applyMapViewMemoryHotFix];
self.mapView.showsUserLocation = NO;
self.mapView.delegate = nil;
self.locationManager.delegate = nil;
[self.mapView removeFromSuperview];
self.mapView = nil;
}
- (void)applyMapViewMemoryHotFix{
switch (self.mapView.mapType) {
case MKMapTypeHybrid:
{
self.mapView.mapType = MKMapTypeStandard;
}
break;
case MKMapTypeStandard:
{
self.mapView.mapType = MKMapTypeHybrid;
}
break;
default:
break;
}
self.mapView.mapType = MKMapTypeStandard;
}
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
[self applyMapViewMemoryHotFix];
}
However, the question I have is, why does the memory not drop to before MapKit levels?
Is there something else I'm missing? Is this expected behaviour? There are no memory leaks judging by the profiler but obviously something isn't right...
Despite the use of the so beloved MemoryHotFix by the SO community regarding this problem, you should be sure that you're not holding any strong reference. As others have told, if you're using (read instantiating) views that hold a reference to the view controller they're in and that same view controller as a reference to that view you might get into a Strong Reference Cycle.
Such situation might block your deinit/dealloc methods rendering your cleanups unnecessary and useless.
As stated in the documentation:
You resolve strong reference cycles by defining some of the relationships between classes as weak or unowned references instead of as strong references.
So be sure to:
Check if your deinit(swift)/dealloc is actually being called (if not, it may indicate a Strong Reference Cycle).
Actually nil the delegates of the MKMapView and LocalitionManager as well as themselves.
Like this:
self.mapView.delegate = nil
self.mapView = nil
self.locationManager?.delegate = nil
self.locationManager = nil
Proof
Having that in mind, here is an example where there is one VC pushing another VC with a MKMapView, every vertical red line means "pushing new VC" and every green line means "popping it":
There's some initial setup (starting at 50 Mb +/-), but future push's don't cause any memory leak as shown. It is worth mentioning that this was captured using a real device. I've tested it using the simulator too and the results are coerent despite the initial setup being much higher (starting at 100 Mb).
Hope you guys can fix it and you might, as well, check your project for Strong Reference Cycles that might compromise your product in the future ;)

iOS - Alloc objects until didReceiveMemoryWarning is called

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.

Autorelease objects in ARC

Suppose in my Database Manager which is singleton.
+ (SWDatabaseManager *)retrieveManager
{
#synchronized(self)
{
if (!sharedSingleton)
{
sharedSingleton = [[SWDatabaseManager alloc] init];
}
return sharedSingleton;
}
}
- (NSArray *)getProductDetails:(NSString *)someString
{
NSArray *temp = [self getRowsForQuery:someString];
return temp;
}
- (NSArray *)getRowsForQuery:(NSString *)sql
{
sqlite3_stmt *statement=nil;
NSMutableArray *arrayResults = [NSMutableArray arrayWithCapacity:1];
//
//Fetching data from database and adds them in to arrayResults
//
return arrayResults;
}
Now from some view controller i am calling function of Database manager like this....
[self getProductServiceDidGetDetail:[[SWDatabaseManager retrieveManager] getProductDetail: #"SomeQuery"]
- (void)getProductServiceDidGetDetail:(NSArray *)detailArray
{
[self setDataSource:[NSArray arrayWithArray:detailArray]];
[self.tableView reloadData];
}
Questions are ...
When arrayResult of getRowsForQuery will release?
Do i need to assign nil to detailArray of getProductServiceDidGetDetail?
Is there any memory leaks?
Suggestion will be appreciated.
ARC does automatic memory management. So it releases everything (your array), when you are done using it.
ARC works by adding code at compile time to ensure that objects live
as long as necessary, but no longer. Conceptually, it follows the same
memory management conventions as manual reference counting (described
in Advanced Memory Management Programming Guide) by adding the
appropriate memory management calls for you.
To help you understand better you might want to read apples docs on ARC.
You will not need to assign nil to the array, nor do you have to worry about memory leaks.
You do not have to (indeed cannot) release instance variables, but you
may need to invoke [self setDelegate:nil] on system classes and other
code that isn’t compiled using ARC.

Examine cocos2d's nodes tree

I am already finishing my game and I am trying to fix some memory issues.
My game has 36 levels and I noticed that when I run it in an iPod, after 20-25 levels my app crashes. I start getting memory warnings and it always crashes when transitioning between scenes.
I have already used instruments to fix every memory leak but this is still happening.
My guess is that cocos is still holding references to old objects.
I would like to find a way to look through the cocos' nodes hierarchy in certain points of my game to make sure everything is ok.
Any idea of how to do that?
I have modified CCTextureCache to log which textures are retained and which others are released, at critical steps in my games, where i force a 'removedUnusedTextures', notably on scene transitions. Whatever you see there should give a hint of where to look in your app. Also, i tend to tag everything with a unique tag, and remember the tags in every class that adds stuff to a CCNode. In the wash (cleanup), i rundown the array of tags, and force remove them.
I ended up doing this:
I added some logic in the CCDirector to "draw" the hierarchy:
-(void) printChildren:(CCNode *)node andLevel:(NSInteger)level {
NSString *tabs = #"";
for (int i=0; i <level; i++) {
tabs = [NSString stringWithFormat:#"%# ", tabs];
}
NSLog(#"%#NODE %#. Children count: %d", tabs, node, node.children.count);
if ( node.children.count == 0 ) {
return;
} else {
for (CCNode *child in node.children) {
[self printChildren:child andLevel:level+1];
}
}
}
-(void) nodeHierarchy
{
NSLog(#"Printing nodeHierarchy! with an stack of %d scenes", [scenesStack_ count]);
for (CCScene *scene in scenesStack_) {
NSLog(#"Scene in stack: %#", [scene class]);
[self printChildren:scene andLevel:0];
}
}
I call nodeHierarchy in the replaceScene method.
It would be great to have a more visual tool, but this worked for me.
Any reason you cannot migrate to SDK 5.0 that has ARC.

Resources