I am trying to load an NSMutableArray with UIImageViews. Everything is going fine with that.
Unfortunately, I have no idea how to use the objects when they are in the mutable array.
Here is some code:
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
NSMutableArray *array = [NSMutableArray new];
[array loadWithObject:(UIImageView *)imageView];
[imageView release];
That kind of sets up what I've done. Here's what I want to do:
[array objectAtIndex:5].center = GCRectMake(0, 0);
but that doesn't work. How can I do this??
OK, I'll explain the problems you're having. The way to do what you are trying to do is the following:
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
NSArray *array = [NSArray arrayWithObject:imageView];
[imageView release];
[[array objectAtIndex:0] setCenter:CGPointMake(0.0,0.0)];
First, there is no method -[NSMutableArray loadWithObject:]. Likewise, for your example, you don't really even need a mutable array. Mutable objects have their place, but I usually try to use immutable ones when it makes sense to; as such, I've used NSArray.
Next, you never need to typecast objects when you're adding them to an array. There are a few reasons wherefore your example didn't work:
You were accessing the sixth (starting at one) object in the array. Was there an instance of UIImageView at that index?
For some reason, dot-notation for getters and setters only works when the compiler knows the type of the object you're sending a message to. Since the type of an object that is coming out of an array is not clear at compile-time, you can't use dot-notation. Instead, just use old-fashioned Objective-C method-sending syntax ("brackets and colons").
Finally, it's Core Graphics, not Gore Craphics: hence the prefix is CG, not GC. Also, -[UIImageView setCenter:] takes a CGPoint, not CGRect. So the function you wanted was CGPointMake.
Best of luck to you! Let me know if this helps clear some things up.
I think you should refer NSMutableArray.
However, I am just giving an overview of NSMutableArray.
NSMutableArray = Next Step (NS) Mutable Array
Mutable means array can be modified as and when required.
Now, This mutable array can hold any kind of objects.
Assume that I want to store strings in an array. I would write following statements.
NSMutableArray *anArray=[[NSMutableArray alloc] init];
[anArray addObject:#"Sagar"];
[anArray addObject:#"pureman"];
[anArray addObject:#"Samir"];
Here, I found that you need to store imageViews in your requirements.
NSMutableArray *anArray=[[NSMutableArray alloc] init];
UIImageView *imgV1=[[UIImageView alloc] initWithFrame:CGRectMake(10,50,60,70)];
UIImageView *imgV2=[[UIImageView alloc] initWithFrame:CGRectMake(10,110,60,70)];
UIImageView *imgV3=[[UIImageView alloc] initWithFrame:CGRectMake(10,170,60,70)];
UIImageView *imgV4=[[UIImageView alloc] initWithFrame:CGRectMake(10,210,60,70)];
[anArray addObject:imgV1];
[anArray addObject:imgV2];
[anArray addObject:imgV3];
[anArray addObject:imgV4];
Now, once ImageViews are added to array, release imageviews as array has it's retain count.
[imgV1 release];
[imgV2 release];
[imgV3 release];
[imgV4 release];
Above code will add images to NSMutableArray
When you use one of the image from an array, just write down this thing
UIImageView *x=[anArray objectAtIndex:0];
Hope Above descriptions work for you.
Add comment, if you don't understood.
Related
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.
So I am making an app really quick, and even though it isn't the best solution, it appears to be working for the most part. So I have a simple image view that pulls an image out of an NSMutableArray arr. I create the array and populate it inside of ViewDidLoad in this manner:
arr = [[NSMutableArray alloc] init];
[arr addObject:[UIImage imageNamed:#"181940jpg"]];
[arr addObject:[UIImage imageNamed:#"168026.jpg"]];
[arr addObject:[UIImage imageNamed:#"168396.jpg"]];
[arr addObject:[UIImage imageNamed:#"168493_.jpg"]];
ANd I continue doing that for 130 images. Obviously this is a big array. I don't know if this is like a really bad way to do it, but if it is, I am open to suggestions! As I go through the array with some simple back and forward buttons pulling images out of the array based on a simple counter variable things work ok until image 45-ish. The app breaks down and the console says this:
* ERROR: ImageIO 'ImageProviderCopyImageBlockSetCallback' header is not a CFDictionary...
Would it help if I broke my images up into separate arrays? Am I just putting too much into the array? What am I missing here, trust me, I am all ears.
Thanks
EDIT: Here is some more information
This is how I am sifting through the array, using a UISegmentedControl set on the top of the screen:
-(void) pickedOne{
if(segmentedControl.selectedSegmentIndex == 1){
NSLog(#"hey");
if(position < [arr count]-1){
position++;//This is my global counter variable
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
}
}else if(segmentedControl.selectedSegmentIndex ==0){
if(position >0){
position--;
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
}
}
}
It doesn't appear to be a problem with memory management, but then again, I don't consider myself a pro at that by any means...
In terms of the mutable array, what you put into it is just a pointer to some other object. It is those other objects you have to worry about and, yes, you have too many of them (more likely than not).
ERROR: ImageIO 'ImageProviderCopyImageBlockSetCallback'
header is not a CFDictionary...
Would it help if I broke my images up
into separate arrays? Am I just
putting too much into the array? What
am I missing here, trust me, I am all
ears.
That sounds more like you have an over-release problem and are passing something bogus to the ImageIO APIs.
And, in fact, that is exactly what you have:
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
That release is spurious; it does not balance a retain anywhere in your code. objectAtIndex: does not return a retained object and the UIView will take care of retaining/releasing the image internally.
Remove that release (and the other one, too)!
You still need to worry about memory consumption. At a size of 42K each (not an unreasonable size, but entirely made up), 130 images will weigh in at ~6MB or so, prior to any decompression or other expansion that occurs as a part of storage.
The devices are quite memory constrained.
I have used following 2 patterns to create a view.
#property (retain, nonatomic) SomeView* someView;
...
// First pattern
self.someView = [[SomeView alloc] initWithFrame frame];
// Second pattern
SomeView* aSomeView = [[SomeView alloc] initWithFrame];
self.someView = aSomeView;
[aSomeView release];
Now, looking back at this code, the first pattern's method should be changed to
self.someView = [[[SomeView alloc] initWithFrame frame] autorelease];
shouldn't it?
I feel dumb :(
Look at it like this:
[[SomeView alloc] initWithFrame: frame];
The above line creates an object and gives it a retain count of 1.
self.someView = [[SomeView alloc] initWithFrame: frame];
This line leaves it with a retain count of two, because the someView property is declared with retain:
#property (**retain**, nonatomic) SomeView* someView;
So, doing it this way leaves your someView property pointing to an object with retain count of 2. You can do it this way if you add an autorelease call to it:
self.someView = [[[SomeView alloc] initWithFrame: frame] autorelease];
Your second pattern is better, if you ask me. You create an object with a retain count of one. You assign it to a retaining property (now it has a retain count of 2) and then you release the original variable, leaving the object again with a retain count of 1. It's three lines where you might want only one, but it makes sense in the right context. Additionally, it's usually best to avoid using autorelease outside of an alloc or copy method since its usually an indication you don't fully understand memory management in Obj-C.
And as a commenter said in the comments to the question, don't feel dumb. None of this is intuitive at first. Nobody picks up a guitar and plays like Hendrix their first time.
Yes, you are right. autorelease means "release a bit later".
Yes, I think you should change that. With self.someView = you are calling the setter which increases the retain count.
Now, looking back at this code, 1's method should be changed to self.someView = [[[SomeView alloc] initWithFrame frame] autorelease];
shouldn't it?
correct
a)
SomeView * view = [[SomeView alloc] initWithFrame:frame];
self.someView = view;
[view release], view = nil;
b)
self.someView = [[[SomeView alloc] initWithFrame:frame] autorelease];
many people prefer b, simply because it is less to type.
i prefer an approach similar to a because:
defects (such as over-releasing) are often exposed near the call site, rather than when the pool is destroyed (this often means you have to load up Instruments in Zombie mode to locate the callsite)
it performs better and minimizes memory usage (in general, but not much in this specific case)
you have more opportunity to check for invalid states and results
you have a chance to init/configure the view/object for its usage before adding it to self, which is usually preferred
The data source for my table view is a plain NSMutableArray that will be populated as the app runs, but will be empty when the Table View first loads. The class interface looks like this...
#interface ViewController_iPhone : UITableViewController {
NSMutableArray *serverList;
}
#property (retain, readonly) NSMutableArray *serverList;
#end
My questions are...
Currently, I initialize it in the viewDidLoad method like so...
serverList = [[NSMutableArray alloc] initWithCapacity:1];
I do it this way because the array needs to be valid in order for my numberOfRowsInSection method to avoid crashing when reading the count of the array (which will be zero) when the view first loads. My current approach of using initWithCapacity just feels a little clunky since I just need an empty, but valid array object that will return a count value of zero when the view loads. How should I be initializing my serverList array?
While playing around, I noticed that when I try and initialize the serverList array this way...
serverList = [NSMutableArray arrayWithCapacity:1];
it crashes on that line. Why?
Thanks in advance for all your help!
Here's a key concept to learn about member variables and properties: Member variables are not the same as properties.
That is, when accessing a member variable in your class:
serverList = [NSArray array]; is not the same as self.serverList = [NSArray array];
serverList by itself means you're accessing it directly.
self.serverList means you're using the getter/setter methods to access it.
Normally this isn't that big of a deal when dealing with basic variable types. However, when your property uses retain or copy, that means your setter method will automatically retain it when you use it, but it won't do such when you access it directly.
That means:
serverList = [NSArray array]; will not retain the array.
self.serverList = [NSArray array]; will retain the array.
It should be noted that [NSMutableArray arrayWithCapacity:1]; (and 99% of other methods that aren't alloc) will return an object that is autoreleased. If you want to keep it for later use, as you need to in this case, then you must retain it in some form or fashion.
I somehow missed the simplest approach and found that when I simply create the array like so...
serverList = [[NSMutableArray alloc] init];
and release it in the dealloc method, everything works great!
Note that capacity does not mean any objects are in the array. Using -initWithCapacity: simply sets aside a chunk of space for the array. Nothing is in the array even with a non-zero capacity.
The following initializers:
self.serverList = [[[NSMutableArray alloc] init] autorelease];
self.serverList = [[[NSMutableArray alloc] initWithCapacity:capacity] autorelease];
self.serverList = [NSMutableArray array];
self.serverList = [NSMutableArray arrayWithCapacity:capacity];
should all work, however.
Make sure you specify self so that the property is accessed. Using serverList by itself will not work.
Make sure you autorelease any alloc-init of a property which you are retaining, otherwise you will have a memory leak.
You do not need an array with a specified capacity. This is just a performance convenience for setting aside contiguous memory, and useful if you have a rough idea how much space your array will need up-front. An array that is created through alloc-init will also be empty, with a count of zero.
I'm doing an iPhone application which uses a navigation control to browse through some data. This data is stored in a sqlite database and I have a class which gets it and returns it in a NSMutableArray of NSStrings.
The problem is that in the first screen of the navigation everything works prefectly, but in the second screen (another view which is pushed) the same code fails because the NSMutableArray gets corrupted. In the debugger I can see that it is returned correctly, but when it's time to use it the pointers have become corrupted and the application crashes.
I have put breakpoints in all my functions, and I can't see anywhere where it can get corrupted. And as the first view, which uses the same exact code, even accesing the same eact tables, works correctly I don't really know where to look.
If anyone want to have a look at the code I have uploaded it to my site: http://sachafuentes.com/iBacus.zip
Thanks in advance.
UPDATE:
The problem lies in the function where I get the data, which looks like (this is a simplified version with some pseudo-code).
-(NSMutableArray *) getPaises {
NSMutableArray * paises;
paises = [[NSMutableArray alloc] init];
while( get new row ) {
NSString *aPais = get the value;
[paises addObject:aPais];
[aPais release];
}
return paises;
}
If I comment out [aPais release] everything works, but to me this looks like a memory leak, as the NSString won't be released.
Okay, here's the problem:
NSString *aPais = [NSString stringWithUTF8String:(char*)sqlite3_column_text(compiledStatement, 0)];
By convention, any time that you see an alloc and an init, you need a release somewhere down the road.
By convention, any time that you use an xWithX: style method, the resulting object will be released for you.
Therefore, this statement:
[aPais release];
will cause your method to crash, as the object is released before it should be. Just remove this line, set your NSMutableArray instance to be autorelease-d and you should get better results.
Look for wrong memory management, that's the likeliest cause for crashes. I think you release your objects somewhere where you shouldn't and therefore you have dangling pointers in the array.
Update1
[aPais release] is wrong, as you don't retain it anywhere in this method. Returned values should always be autoreleased. You should have the same amount of retain as release in your code.
(Some people argue that a retain can also be an alloc init, but I always try to use alloc init autorelease, but this is a matter of style.)
BTW
You should autorelease your array, you're only retaining it here with [[alloc] init].
Any object that you alloc and init must be release-d when you're finished with it, or you will have a memory leak.
Since you are passing the reference outside the scope of the getPaises method, you must autorelease the NSMutableArray, or you will have a memory leak:
paises = [[[NSMutableArray alloc] init] autorelease];
Can you please clarify the step here:
NSString *aPais = get the value;
It's not clear what happens in "get the value" and I suspect this is one cause of instability.
I see that the code is (verbatim)
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *aPais = [NSString stringWithUTF8String:
(char*)sqlite3_column_text(compiledStatement, 0)];
[paises addObject:aPais];
[aPais release];
}
...and it's exactly as #gs puts it. aPais is autoreleased and should not be released.
You can also use a convenience constructor to initialize the NSMutableArray:
NSMutableArray *paises = [NSMutableArray array];
This is equivalent to doing:
NSMutableArray *paises = [[[NSMutableArray alloc] init] autorelease];