Loading CountViewController - Displaying the stored count?


#1

Hi folks, I’ve got the Core Data project pretty much complete and working, but it looks like there’s at least one thing that it should be doing but isn’t.

I can set the count for an asset without problem, and when I return to the assets listing view, that count is reflected so I know it’s being saved in the Core Data stores. However, when I return to the count view for that same asset, the count number which I previously entered is not displayed in the TextField.

I’d like it to be displayed in the TextField, but I’m not quite sure how to pass it across from the Core Data store to the CountView. There is already a method which is setting this value upon the loading of the view:

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    [self updateInterface];
    [numberField becomeFirstResponder];
    }

  • (void)updateInterface
    {
    NSString *prompt = [NSString stringWithFormat:@"%@: %@",
    [asset valueForKey:@“label”],
    [location valueForKey:@“label”]];

    [promptField setText:prompt];
    [numberField setText:[count stringValue]];
    }

So you can see the numberField is supposed to get set to the count value. However, that count value isn’t actually getting set when the CountViewController is instantiated (tableView:didSelectRowAtIndexPath from AssetListViewController.m):

  • (void) tableView(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip
    {
    countViewController = [[CountViewController alloc] init];
    [countViewController setLocation];
    NSManagedObject *asset = [assetList objectAtIndex:[ip row]];
    [countViewController setAsset:asset];
    [[self navigationController] pushViewController:countViewController animated:YES];
    }

So, how the heck do I dig the count value for that particular asset item out of the Core Data store so I can attach it to the CountViewController object and have it displayed? That’s my basic problem. I just don’t know how to grab the right data so I can include it with the object.


#2

Well, nevermind. I guess I figured this one out for myself. I added these two lines to tableView:didSelectRowAtIndexPath:

NSManagedObject *inventory = [self inventoryForAsset:asset];
[countViewController setCount:[inventory valueForKey:@“count”]];

And this carried the stored count value over into the CountViewController perfectly. However, it provoked a followup question from me. I’m still not 100% solid with all the memory management concepts that iPhone programming involves. Should I be releasing my newly instantiated inventory object after I’ve added its count value to the countViewController object? I’m not using it for anything else after that, so common sense is telling me to release it.

However, the only reason I might be hesitant to release it because there is another NSManagedObject declared in that same method which is not released. This code comes from the book, so that makes me think maybe there is some reason not to release those objects. Here’s the method code which comes directly from the book:

  • (void) tableView(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip
    {
    countViewController = [[CountViewController alloc] init];
    [countViewController setLocation];
    NSManagedObject *asset = [assetList objectAtIndex:[ip row]];
    [countViewController setAsset:asset];
    [[self navigationController] pushViewController:countViewController animated:YES];
    }

So I’ve added my inventory stuff to it in order to grab that count value. But why isn’t the asset object getting released here? I know it’s getting added to the countViewController. Does this mean that countViewController’s setAsset method (synthesized) isn’t retaining the asset object when it gets added? Because that’s the only reason I can think of for this object not to be released. It just seems like a memory leak to me.

Oops. I guess I have to take most of that back. I’ve just noticed that neither of those NSManagedObjects is actually having memory allocated in this method, which tells me that the memory management is being taken care of by the methods used to create/grab them (objectAtIndex and inventoryForAsset).

Unfortunately, I still don’t understand exactly how that works, either. I know there is a principle that tells me that if I don’t allocate memory to an object in a method, I don’t have to release it there. So if it’s created by some inherited method, I can trust that method to control the memory management. But how does it actually happen? When I create my inventory object by calling inventoryForAsset, I guess I’m really just getting a pointer to an object that already exists, whose memory footprint is taken care of elsewhere.

I’ve really gone in circles here, and I guess I don’t actually have any cogent questions at all. What a ramble!


#3

The trick here is to differentiate between pointers to objects and objects.

When you get a pointer to an object back from a method that doesn’t say alloc, copy, new or retain in it, you don’t own that object. You just have a pointer to that object. You can use this pointer (which is really just a number) to essentially pass it around to other objects.

Those other objects may want to take ownership of the object pointed to because it wants to guarantee that it exists later on.

So, in these cases, let’s break it down:

In your first case, count should just be an int so it won’t matter. But pretend it is an object:
inventoryForAsset: returns a pointer to some NSManagedObject. This object exists, we know it exists, but at this point, we don’t own it (it didn’t say alloc, copy, new or retain). We just have a pointer to it. Then we give it to countViewController, and lets say the count property is set to retain. This object is now owned by self and the countViewController. You don’t have to do any additional cleanup in this method, because you never took ownership.

The second example is the same thing. You grab an asset out of the assetList - but really, you are just getting a pointer to an asset object that the assetList was holding on to. Then you give it to countViewController - which retains it and keeps it around - but you never took ownership.

For some people, it helps to think about this theoretically. For others, perhaps like yourself, it helps to think about it at a more computer-y level.

The following line of code:

NSManagedObject *asset = [assetList objectAtIndex:[ip row]];

Allocates exactly 4 bytes from the stack for the variable named asset. Then, the location in memory of the [ip row]th object in assetList is copied into the variable asset - this location in memory is such an integer. A simple four bytes, that technically doesn’t know much about the asset object itself, other than where it resides in memory.

When the method that this line of code is in ends, any stack-allocated memory is returned back to the stack. That means, any local variables are destroyed. But once again, this local variable was just holding the address of an object, not the object itself.


#4

Thanks, Joe. I’ve been writing in VB.NET for several years so it’s been a long time since I’ve had to deal with anything involving memory management or pointers. I feel like I do understand most of the basic principles, but your reply raises another question for me.

It looked like you were saying that the declaration of the asset pointer variable takes up 4 bytes of memory. It also sounds like you’re saying that since it’s a local variable, it is destroyed and its memory space returned to the stack once the method concludes. Doesn’t the same thing happen with actual objects which are instantiated in the method as well? And yet we are required to make sure to release those at the end of the method if we have allocated them in the body of that method.

Is this just because the compiler knows not to automatically destroy a variable that still has a valid retain count? And yet, why not simply pass pointers to methods where you want to use an object that you’ve just declared rather than using the retain count at all? Then it would be the pointers in the other methods that would be automatically destroyed when they conclude, after which the originally declared object in the original method would also be destroyed when that method finally ends.

Hmm, I think I probably need to read the memory management chapter again…


#5

All Objective-C objects are created on the heap by sending the message alloc to a class.

So, in a local method, you will have something like:

Possession *p = [[Possession alloc] init];

You are allocating two chunks of memory here: a variable named p that exists on the stack and an object of type Possession that exists on the heap. There are no stack-based objects in Objective-C. (Except, somewhat technically, blocks, but those are special.)