iPad large NSArray - initWithObjects vs. ArrayWithObjects - iphone

can anyone clear this up for me ?
I am building an iPad App that has a TableViewController that is supposed to show something between 1000 and 2000 strings.
I have those NSStrings in a Singleton.
In the init Method of the Singleton I initialize an Array that holds all the data ( does not have to be the final way to do it - was just a quick copy and paste for testing )
I did an self.someArray = [[NSArray alloc]initWithObjects: followed by the large number of strings, followed by nil.
that worked fine in the simulator - but crashed with bad access on the iPad right on Application startup
If I use the convenience method [NSArray arrayWithObjects:instead - it works fine.
I looked into Instruments and the overall memory footprint of the App is just about 2,5 MB.
Now I don't know why it works the one way but not the other.
EDIT:
#import "StaticValueContainer.h"`
static StaticValueContainer* instance = nil;
#implementation StaticValueContainer
#synthesize customerRatingKeys;
+(StaticValueContainer*)sharedInstance
{
if (instance == nil){
instance = [[StaticValueContainer alloc]init];
}
return instance;
}
-(id)init
{
if ( ( self = [super init] ))
{
[self initCustomerRatingKeys];
}
return self;
}
-(void)init customerRatingKeys
{
self.customerRatingKeys = [[NSArray alloc]initWithObjects:
#"string1",
....
#"string1245"
,nil
}
as I said: it crashes on the device with self.customerRatingKeys = [[NSArray alloc]initWithObjects:
but works with *self.customerRatingKeys = [[NSArray arrayWithObjects...`

Well, there isn't much difference between them: arrayWithObjects returns an auto-released array that you don't need to release yourself (unless you subsequently retain it), and initWithObjects returns an array you must then release to avoid a memory leak. Performance wise there is no difference between them.
I would suggest if you're getting a bad access error using initWithObjects but not with arrayWithObjects there might be some sort of memory management error in your code. If you post the code itself you'll probably get a more exact response.

Related

How to release an object in a forin loop?

I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.

EXC_BAD_ACCESS when setting ivars directly (without using accessors) inside -init method, why?

I've spent about 10 hours trying to find this bug that was causing my app to crash, and it was in the last place I looked (well it would have been, but last place I ever expected it to be).
Originally I thought I had memory management issues (unbalanced retain/release) because the crash would happen every time I sent -removeAllObjects to an NSMutableArray filled with my custom objects. The crash wouldn't happen the first time -removeAllObjects was called. I could clear the array once, repopulate it, and then on the second clear, I would get a EXC_BAD_ACCESS. This is when my array got populated with 3 objects on the first "cycle", and 3 again on the second "cycle". When I was storing only 1 object in the array in each cycle it took 4 cycles to crash (on the 4th call of -removeAllObjects).
I FINALLY realised that the crash would go away if I changed the -init method of my custom object. Here's the -init implementation; all 4 ivars are synthesized properties with (nonatomic, retain), all of type (NSString *) except for icon which is an (NSNUmber *)
-(id)init {
if (self = [super init]) {
ip = #"";
mac = #"";
vendor = #"";
icon = [NSNumber numberWithInt:0];
}
return self;
}
Changing it to this fixed the bug:
-(id)init {
if (self = [super init]) {
self.ip = #"";
self.mac = #"";
self.vendor = #"";
self.icon = [NSNumber numberWithInt:0];
}
return self;
}
I've read that one should't use the accessors in the -init method because it can cause trouble (e.g. with subclassing).
If someone could explain to me why my bug goes away when I use accessors I would be so incredibly grateful! Seriously this has been driving me nuts, was up till 5am last night because of this.
You are assigning, but not retaining, the instance variables directly. When you use the dot syntax, you are triggering the retain part of the synthesized property and, thus, are retaining them.
-(id)init {
if (self = [super init]) {
ip = #"";
mac = #"";
vendor = #"";
icon = [[NSNumber numberWithInt:0] retain];
}
return self;
}
That should likely fix the problem (though, I'm slightly surprised, I thought 10 was still in NSNumber's instance cache. Maybe not.).
Technically you should also retain the #"" strings, but you can get away with not doing so because such strings are a special cased constant string that comes out of the compiled executable (as a private subclass of NSString that overrides to not respond to retain/release/autorelease).
The memory management guide covers this in detail. For anyone new to the platform, I would suggest re-reading it once a month (no, really -- interleaving your coding with an occasional re-read of the docs will often reveal subtle details that you didn't have enough experience to grok before. I still re-read the basic guides on a semi-annual basis.)

iphone NSString Array

I declared
NSString *dayinfield[43];
and fill it in
-(void)DrawDemo {
dayinfield[2] = #"hallo";
dayinfield[3] = #"test";
// also i can read it
NSLog (#"show: %#",dayinfield[2]);
//works fine
}
but when i like to read its content in another function (same class)
-(void)ReadData
{
NSLog (#"show: %#",dayinfield[2]);
// I get random infos or “EXC_BAD_ACCESS
}
How do I initialize the NSString Array correct so I can reach its content in each of my functions??
Thanks
chris
If you only assign literals to the array elements, this should not be a problem. But if you use other strings, you have to retain the instances manually when using a C array.
By the way: Objective-C methods start with a lowercase letter.
This would happen if you never initialized the array (or the parts of it you are accessing) - if you haven't called -DrawDemo before -ReadData or used different indices than the ones posted here, the array would simply contain garbage values.
Try to initialize the array contents to nil or #"" in your initializer method and see if the problem persists.
Alternatively consider using a suitable Cocoa container.
It's memory is probably being released before your second call. Assuming you have declared dayinfield as an ivar (and the fact that you don't get bad access all the time) your string aren't properly retained.
Initialise the strings like this:
dayinfield[2] = [[NSString alloc] initWithString:#"hallo"];
dayinfield[3] = [[NSString alloc] initWithString:#"test"];
and you should release them after you're class is being deallocated (See Memory Management Guide).
Also, obviously it depends on what you want to do, but it might be easier if you use NSArray instead of C arrays.
What you have in the OP should work although it is an exercise in sheer masochism to use old school C arrays with objects.
I ran this code:
#interface TestClass : NSObject {
NSString *a[1];
}
- (void) drawDemo;
- (void) readData;
#end
#implementation TestClass
- (void) drawDemo{
a[0]=#"A Zero";
a[1]=#"A One";
}//------------------------------------- (void) drawDemo------------------------------------
- (void) readData{
NSLog(#"a[0]=%#,a[1]=%#",a[0],a[1]);
}//------------------------------------- (void) readData------------------------------------
#end
TestClass *tc=[[TestClass alloc] init];
[tc drawDemo];
[tc readData];
... and got this output:
a[0]=A Zero,a[1]=A One
Your problem is elsewhere in your code. There is no compelling reason to use C arrays with objects. You gain nothing and you have to watch them like a hawk to prevent errors.

Memory management in objective-c

I have this code in one of my classes:
- (void) processArray
{
NSMutableArray* array = [self getArray];
. . .
[array release];
array = nil;
}
- (NSMutableArray*) getArray
{
//NO 1:
NSMutableArray* array = [[NSMutableArray alloc]init];
//NO 2:
NSMutableArray* array = [NSMutableArray array];
. . .
return array;
}
NO 1: I create an array and return it. In the processArray method I release it.
NO 2: I get an array by simply calling array. As I'm not owner of this, I don't need to release it in the processArray method.
Which is the best alternative, NO 1 or NO 2? Or is there a better solution for this?
The method should return an autoreleased array, NO 2 is the better choice. The reason for this is that when you call the method with
NSMutableArray* array = [self getArray];
you will expect as a matter of convention to not have to release the array. If you want to take ownership of it, you retain it, but otherwise you shouldn't have to care about it. In Cocoa, you only take ownership by explicitly sending alloc or retain or copy (or new). Since the processArray method doesn't do that, it shouldn't have to take care of releasing the array.
So you should use NO 2, and also you should remove these two lines:
[array release];
array = nil;
If the array and its contents use a lot of memory or its used lots of times, you'll want to release them straight away, so use option 1. According to the Objective-C guidelines, you'll want to prefix the word "new" to your subroutine name instead of "get" in that case.
If on the other hand, you want to reduce the number of lines of code that say simply [array release]; or similar then use option 2.
It is simply a balance between reducing lines of code, and reducing unnecessary temporary memory use.
Whilst the autorelease pool will help in reducing memory leaks and make your code smaller, sometimes you need to explicitly release everything as it goes out of use to keep the use of memory down.
HTH
EDIT
Ah - I stand corrected. Reading the iPhone version of the Memory Management Programming Guide for Cocoa I see that the iPhone guidelines are to use a prefix of "new..." so for example "newArray" in this case, if the caller is supposed to manually release and NOT a prefix of "create...". "Creating" can refer either to creation of manually released or of automatically released objects and so would be ambiguous. Text corrected above.
- (void) processArray
{
NSMutableArray* array = [[self getArray] retain];
//Now you are the owner of array, so you take care to release it
. . .
[array release];
array = nil;
}
- (NSMutableArray*) getArray
{
//create a new array
//temporarily the method owns the array
NSMutableArray* array = [[NSMutableArray alloc]init];
//fill in here with elements or what you want
..........
[array autorelease];
//autorelease here says "I don't own the result
//if anyone cares about it, he should retain it himself
return array;
}
So in short when you create new objects you should autorelease them before returning.
Because if the calling method wants to use the result, the calling method should take care
of retaining and releasing the result.
It's always good to run the Klang static analyzer for this issues, when you are not really sure in your retaining/releasing code : http://clang-analyzer.llvm.org/

Unnecessary temporary variables when setting property values?

I'm following a book on iPhone development and there is a particular pattern I keep seeing in the example code that doesn't make much sense to me. Whenever a property is set they first assign a pointer to the new value for the property, then set the property to the pointer, then release the pointer. Example:
Interface:
#interface DoubleComponentPickerViewController : UIViewController {
NSArray *breadTypes;
}
#property(nonatomic, retain) NSArray *breadTypes;
#end
Class method:
- (void)viewDidLoad {
NSArray *breadArray = [[NSArray alloc] initWithObjects:#"White", #"Whole Wheat", #"Rye", #"Sourdough", #"Seven Grain", nil];
self.breadTypes = breadArray;
[breadArray release];
}
Is there any reason to do this instead of just doing the following?
- (void)viewDidLoad {
self.breadTypes = [[NSArray alloc] initWithObjects:#"White", #"Whole Wheat", #"Rye", #"Sourdough", #"Seven Grain", nil];
}
Thanks for the light that will no doubt be shed :)
Let me try and explain it in a different way.
A method that has alloc, copy or new in its name will allocate memory for an object, and gives ownership of that object to the caller, and it is the caller's responsibility to release that memory.
In your viewDidLoad method, you call a method that gives you ownership of an object. It is your method's responsibility to release it. However, before you do that, you want to do something with it - after all, that's why you alloc'ed it, not to just release it, but to do something useful with it.
Regardless of what it is that you want to do with it, you have to release it (or autorelease it*). In this case your use of the object is to pass it to self.breadTypes. self.breadTypes may not look like a method, but it is (it is a setter). You pass it breadArray. It does what it needs to with it. It might retain it for use later, or it might copy some info out of it, or make a copy of the entire thing. Your viewDidLoad doesn't really care. It assumes that self.breadTypes does what it needs to and when it returns, it doesn't care what you do with breadArray.
And what you do with it, is what you have to do with anything that you own - release (or autorelease* it).
That's why you have to use the temp variable, breadArray. You can't quite release the results from alloc on the same line, since the object would get released before self.breadTypes can have at it:
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White", ..., nil] release];
Thus you are forced to assign to a temp variable, pass it to self.breadTypes, and then release the object that is saved in breadArray.
Now, you could try to do it this way so you don't use a temp variable:
- (void)viewDidLoad {
self.breadTypes = [[NSArray alloc] initWithObjects:#"White", #..., nil];
[self.breadTypes release];
}
but that is not very efficient since you are calling yet another method (self.breadTypes as a getter) that you didn't really need to if you have just stored the value in a temp variable.
*Now, as a responder said, you could use autorelease for an alternative version:
- (void)viewDidLoad {
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White", ..., nil]
autorelease];
}
Apple urges us to think twice about whether we want to use autorelease vs. release. Autorelease may not be the best choice for every situation. I personally like to clean up after myself as soon as I possibly can, and not use autorelease needlessly. Autoreleased objects get released at the end of the execution of the run loop, for example soon after viewDidLoad returns. You should read up a bit more about autorelease (and memory management on the iPhone which is slightly different than MacOS X Cocoa), as I am oversimplifying it all.
BTW: If you retain an object, you are assuming ownership of it, and you will have the same responsibility again, to release it after you are done with it.
Yes. Those methods are alloc'ing the variables so they must be released. The fact that the property has a retain attribute means that when you say #synthesize breadTypes; the compiler is actually generating a setBreadTypes that properly releases the current breadType member and retains the new one. Thus your function must not retain the variable it alloc'ed.
You could, however write:
- (void)viewDidLoad {
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White",
#"Whole Wheat", #"Rye", #"Sourdough",
#"Seven Grain", nil]
autorelease];
}
You'll want to brush up on Cocoa Memory Management