Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 608
n3on Avatar asked Mar 23 '11 19:03

n3on


People also ask

How do I iterate over an object key?

You have to pass the object you want to iterate, and the JavaScript Object. keys() method will return an array comprising all keys or property names. Then, you can iterate through that array and fetch the value of each property utilizing an array looping method such as the JavaScript forEach() loop.

Can you loop through an object?

The better way to loop through objects is first to convert the object into an array. Then, you loop through the array. You can convert an object into an array with three methods: Object.

How do I iterate through a JSON object?

Use Object.values() or Object. entries(). These will return an array which we can then iterate over. Note that the const [key, value] = entry; syntax is an example of array destructuring that was introduced to the language in ES2015.


2 Answers

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.

like image 82
BoltClock Avatar answered Sep 29 '22 20:09

BoltClock


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.

like image 21
Sherm Pendley Avatar answered Sep 29 '22 20:09

Sherm Pendley