How to manage memory using classes in Objective-C?

This is the first time I have created an application for the iPhone, and I have difficulty managing memory because I have never had to deal with it before.

I have a UITableViewController and everything works fine until I try to scroll down in the simulator. He falls, saying that he cannot allocate so much memory. I narrowed it to the scene:

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Dequeue or create a cell UITableViewCellStyle style = UITableViewCellStyleDefault; UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:@"BaseCell"]; if (!cell) cell = [[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:@"BaseCell"] autorelease]; NSString* crayon; // Retrieve the crayon and its color if (aTableView == self.tableView) { crayon = [[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] getName]; } else { crayon = [FILTEREDKEYS objectAtIndex:indexPath.row]; } cell.textLabel.text = crayon; if (![crayon hasPrefix:@"White"]) cell.textLabel.textColor = [self.crayonColors objectForKey:crayon]; else cell.textLabel.textColor = [UIColor blackColor]; return cell; } 

Here is the getName method:

 - (NSString*)getName { return name; } 

name is defined as:

 @property (nonatomic, retain) NSString *name; 

Now sectionArray is an NSMutableArray with instances of the class in which I created Term in it.

The term has a getName method that returns NSString *. It seems that the problem is where the pencil is set and getName is called. I tried to add autorelease , release, and other similar things, but this just causes the whole application to crash before it starts.

Also, if:

 cell.textLabel.text = @"test"; //crayon; /*if (![crayon hasPrefix:@"White"]) cell.textLabel.textColor = [self.crayonColors objectForKey:crayon]; else cell.textLabel.textColor = [UIColor blackColor];*/ 

Then I get no error, and it all scrolls just fine.

Thanks in advance for your help!

Edit:

Here is the full log when I try to run the application and the error it gives when it crashes:

[The session began on 2010-12-29 04:23:38 -0500.]

[Session started on 2010-12-29 04:23:44 -0500.] GNU gdb 6.3.50-20050815 (Apple version gdb-967) (Tue Jul 14 02:11:58 UTC 2009) Copyright 2004 Free Software Foundation , Inc. GDB is free software covered by the GNU General Public License, and you are welcome to modify it and / or distribute it under certain conditions. Type "show copy" to see the conditions. There are absolutely no guarantees for GDB. Enter "show warranty" for more information. This GDB was configured as "i386-apple-darwin" .sharedlibrary apply-load-rules all Attachment to process 1429. gdb-i386-apple-darwin (1430,0x778720) malloc: * mmap (size = 1420296192) failed (code errors = 12) : cannot highlight area ** set breakpoint in malloc_error_break for debugging scan gdb stack at internal error point: [0] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386- apple-darwin (align_down + 0x0) [0x1222d8] [1] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin (xstrvprintf + 0x0) [0x12336c] Developer / Platforms / iPhoneSimulator.platform / Developer / usr / libexec / gdb / gdb-i386-apple-darwin (xmalloc + 0x28) [0x12358f] [3] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb / gdb-i386-apple-darwin (dyld_in fo_read_raw_data + 0x50) [0x1659af] [4] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin (dyld_info_read + 0x1bc) [0x16858 /] / Platform] /] iPhoneSimulator.platform / Developer / usr / libexec / gdb / gdb-i386-apple-darwin (macosx_dyld_update + 0xbf) [0x168c9c] [6] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexbg -apple-darwin (macosx_solib_add + 0x36b) [0x169fcc] [7] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin (macosx_child17attacht11_tach_11dacht) 11dacht_11ach_attach_11ach_attach_11ach_attach_11ach /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin (attach_command + 0x5d) [0x64ec5] [9] /Developer/Platforms/iPhoneSimulator.platform/Dib/ gdb / gdb-i386-apple-darwin (mi_cmd_target_attach + 0x4c) [0x15dbd] [10] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-dmx 0 (0) 0x17427] [11] / Deve loper / Platforms / iPhoneSimulator.platform / Developer / usr / libexec / gdb / gdb-i386-apple-darwin (catch_exception + 0x41) [0x7a99a] [12] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexecgd / gdb-i386-apple-darwin (mi_execute_command + 0xa9) [0x16f63] / SourceCache / gdb / gdb -967 / src / gdb / utils.c: 1144: internal error: virtual memory has been exhausted: 1420296192 bytes could not be allocated. A problem has been discovered that is internal to GDB, further debugging may not be reliable.

The debugger exited with status 1. The debugger exited with status 1.


Here is the backtrace that I get when I set a breakpoint for malloc_error_break:

 #0 0x0097a68c in objc_msgSend () #1 0x01785bef in -[UILabel setText:] () #2 0x000030e0 in -[TableViewController tableView:cellForRowAtIndexPath:] (self=0x421d760, _cmd=0x29cfad8, aTableView=0x4819600, indexPath=0x42190f0) at /Volumes/Main2/Enayet/TableViewController.m:99 #3 0x016cee0c in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] () #4 0x016c6a43 in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:] () #5 0x016d954f in -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow] () #6 0x016d08ff in -[UITableView layoutSubviews] () #7 0x03e672b0 in -[CALayer layoutSublayers] () #8 0x03e6706f in CALayerLayoutIfNeeded () #9 0x03e668c6 in CA::Context::commit_transaction () #10 0x03e6653a in CA::Transaction::commit () #11 0x03e6e838 in CA::Transaction::observer_callback () #12 0x00b00252 in __CFRunLoopDoObservers () #13 0x00aff65f in CFRunLoopRunSpecific () #14 0x00afec48 in CFRunLoopRunInMode () #15 0x00156615 in GSEventRunModal () #16 0x001566da in GSEventRun () #17 0x01689faf in UIApplicationMain () #18 0x00002398 in main (argc=1, argv=0xbfffefb0) at /Volumes/Main2/Enayet/main.m:14 

--------


Here is my complete source for the UITableViewController class:

 // // TableViewController.m // Enayet // // Created by Filip on 12/27/10. // Copyright 2010 __MyCompanyName__. All rights reserved. // #import "TableViewController.h" #import "EnayetAppDelegate.h" @implementation TableViewController @synthesize crayonColors; @synthesize filteredArray; @synthesize sectionArray; @synthesize searchBar; @synthesize searchDC; - (TableViewController*) initToCall:(NSMutableDictionary*) toUseArray { self = [super initWithStyle:UITableViewStylePlain]; crayonColors = toUseArray; return self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView { if (aTableView == self.tableView) return 26; return 1; } // Via Jack Lucky - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [self.searchBar setText:@""]; } - (NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section { if (aTableView == self.tableView) { if ([[self.sectionArray objectAtIndex:section] count] == 0) return nil; //return [NSString stringWithFormat:@"%@", [[ALPHA substringFromIndex:section] substringToIndex:1]]; return [[ALPHA substringFromIndex:section] substringToIndex:1]; } else return nil; } - (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section { // Normal table if (aTableView == self.tableView) return [[self.sectionArray objectAtIndex:section] count]; // Search table NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", self.searchBar.text]; self.filteredArray = [self.crayonColors.allKeys filteredArrayUsingPredicate:predicate]; return self.filteredArray.count; } // Convert a 6-character hex color to a UIColor object - (UIColor *) getColor: (NSString *) hexColor { unsigned int red, green, blue; NSRange range; range.length = 2; range.location = 0; [[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&red]; range.location = 2; [[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&green]; range.location = 4; [[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&blue]; return [UIColor colorWithRed:(float)(red/255.0f) green:(float)(green/255.0f) blue:(float)(blue/255.0f) alpha:1.0f]; } - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Dequeue or create a cell UITableViewCellStyle style = UITableViewCellStyleDefault; UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:@"BaseCell"]; if (!cell) cell = [[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:@"BaseCell"] autorelease]; NSString* crayon; // Retrieve the crayon and its color if (aTableView == self.tableView) { Term *term = (Term *) [[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; crayon = [term name]; } else { crayon = [FILTEREDKEYS objectAtIndex:indexPath.row]; } cell.textLabel.text = crayon; if (![crayon hasPrefix:@"White"]) cell.textLabel.textColor = [self.crayonColors objectForKey:crayon]; else cell.textLabel.textColor = [UIColor blackColor]; return cell; } // Grouped Tables do not support section indices (even though they kind of do) - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)aTableView { if (aTableView == self.tableView) // regular table { NSMutableArray *indices = [NSMutableArray arrayWithObject:UITableViewIndexSearch]; for (int i = 0; i < 26; i++) if ([[self.sectionArray objectAtIndex:i] count]) [indices addObject:[[ALPHA substringFromIndex:i] substringToIndex:1]]; // [indices addObject:@"\ue057"]; // <-- using emoji return indices; } else return nil; // search table } - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { if (title == UITableViewIndexSearch) return 0; return [ALPHA rangeOfString:title].location; } - (void)goToTwo:(Term*) toGive { EnayetAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; [appDelegate setTerm:toGive]; [appDelegate displayView:2]; } // Respond to user selections by updating tint colors - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (aTableView == self.tableView) { [self goToTwo:[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]]; } } - (void) viewDidLoad { // Prepare the crayon color dictionary NSString *pathname = [[NSBundle mainBundle] pathForResource:@"crayons" ofType:@"txt" inDirectory:@"/"]; NSArray *rawCrayons = [[NSString stringWithContentsOfFile:pathname encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByString:@"\n"]; self.crayonColors = [NSMutableDictionary dictionary]; self.sectionArray = [NSMutableArray array]; for (int i = 0; i < 26; i++) [self.sectionArray addObject:[NSMutableArray array]]; for (NSString *string in rawCrayons) { [self.crayonColors setObject:CRAYON_COLOR(string) forKey:CRAYON_NAME(string)]; NSUInteger firstLetter = [ALPHA rangeOfString:[string substringToIndex:1]].location; if (firstLetter != NSNotFound) [[self.sectionArray objectAtIndex:firstLetter] addObject:[[Term alloc] initToCall:CRAYON_NAME(string)]]; } //NSLog(@"%@", sectionArray); // Create a search bar self.searchBar = [[[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)] autorelease]; self.searchBar.tintColor = COOKBOOK_PURPLE_COLOR; self.searchBar.autocorrectionType = UITextAutocorrectionTypeNo; self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone; self.searchBar.keyboardType = UIKeyboardTypeAlphabet; self.tableView.tableHeaderView = self.searchBar; // Create the search display controller self.searchDC = [[[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self] autorelease]; self.searchDC.searchResultsDataSource = self; self.searchDC.searchResultsDelegate = self; } @end 

------


Here is my Term.m file:

 // // Term.m // Enayet // // Created by Filip on 12/27/10. // Copyright 2010 __MyCompanyName__. All rights reserved. // #import "Term.h" @implementation Term @synthesize name; - (Term*)initToCall:(NSString*) toSetName { name = toSetName; return self; } - (NSString*)getName { return name; } -(void)dealloc { [name release]; [super dealloc]; } @end 
+1
memory-management objective-c iphone
source share
4 answers

In your initToCall: method initToCall: replace:

 name = toSetName; 

from:

 name = [toSetName retain]; 

Even if you set the property as the save property, your original value is not saved because you assign it directly. In Cocoa terms, you never “take responsibility”. Since the string value that you passed in does not belong to you, it ends with you, and that is why you see the crash. If you assign ivars directly in init methods, you must remember to save or copy them yourself. Another option is to use accessors:

 self.name = toSetName; 

In this case, you will automatically call your synthesized setter, which will save the property for you (since you specified it as the "save" property).

EDIT: Also, if you are not directly related to your problem, this is a good form to always call the initializer assigned to your superclasses from your assigned initializer. Thus, the general template will look like this:

 - (id)initToCall:(NSString*)toSetName { if( (self = [super init]) ) { // call NSObject designated initializer // do our initialization name = [toSetName retain]; // take ownership of this string } return self; } 
+1
source share

Firstly, if there is a failure, there is a back trace. Send it. Saw your editing; set a breakpoint on malloc_error_break and post backtrack. Typically, such a malloc () failure indicates that you have either run out of your 32-bit address space, or you indicated that the bogon value is passed to malloc.

Secondly, if you have a memory management problem, tools can help you track it. Launch the application using the Allocations tool and try to scroll. It should be very obvious what stands out so often to cause such a crash.

You said:

The problem, apparently, is the part where the pencil is set and getName is called.

If the problem is with getName , send getName code. Be that as it may, there is nothing in this code that obviously consumes memory excessively.

The getName method looks fantastically simple. :)


In the debugger console, if you type b malloc_error_break , it will set a breakpoint. Or you can set a breakpoint as a “symbolic breakpoint” in the Xcode debugger window.

Tools are another tool; you can run "Run -> Run With Performance Tool -> Allocations" to run the application under the tools.

However, given that a completely dummy value is passed to malloc (), I suspect something else went wrong.


This backtrace is actually quite useful. It indicates that the malloc () error occurs when Cell tries to highlight something related to setting text.

And you also said that if you add cell.textLabel.text = @"test"; then the error will stop happening.

My best guess; you either re-release or corrupt the line that should be displayed as text cell.textLabel .

In particular, I would bet that crayon or damaged in certain circumstances. Try adding NSLog(@"crayon %@", crayon); into your code and see if it is also barfs. And, as someone suggested, try starting with Zombie detection enabled (Run -> Run with Performance Tools -> Zombie Detection, or whatever it is called).

+2
source share

In general, this is not a direct cause of your failure, but an indication of the nature of use, which is alien to standard iOS templates.

You say that you have:

 - (NSString*)getName { return name; } 

and

 @property (nonatomic, retain) NSString *name; 

This is simply wrong; there is no need to define a getName method if you have defined a property for name . Just use the name method, either with a direct call or with the dot syntax.

That is it:

 crayon = [[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] getName]; 

Must be:

 crayon = [[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] name]; 
+2
source share

Your getName method is invalid. It should return an object with a save value of +0 (saved and auto-implemented in the context of the caller’s auto-advertisement).

 -(NSString *)getName { return [[name retain] autorelease]; } 

As others have said, using the @synthesize name to create a name method (NSString *) and call which is a better idea instead.

The error causing the failure you are observing is almost certainly in the code where the name is indicated; I suspect that you did not save the value correctly. Again, if you use the name @synthesize and call the generated method - (void) setName: (NSString *) to set the name, it will work correctly.

0
source share

All Articles