Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Byzanti

macrumors newbie
Original poster
Jun 6, 2008
26
2
Hi

If I call an instance variable (set with @property) from another object, does that entire variable get reloaded into memory?

For example, I allocate an object at the start of my code, and part of the initialisation loads a large NSDictionary into memory.
EXSomeObject *someObject = [[EXSomeObject alloc] init];

If I later try and access a key from this dictionary from outside EXSomeObject itself, does the whole dictionary get reloaded elsewhere?

Eg NSLog(@"%@",[[someObject someDictionary] objectForKey:mad:"someKey"]);

Even if it doesn't, it this considered bad practice for some reason or other?

Many thanks!
 
Last edited:

lee1210

macrumors 68040
Jan 10, 2005
3,182
3
Dallas, TX
You haven't really explained how your dictionary gets from one place to another. If you're just copying the pointer, the object is not getting copied. If you assign to a property that uses "copy" than the object will get copied and use more memory. If you manually copy, same issue. If you're pointing to the same memory from multiple pointers, no problem.

If you show more actual code we can tell you specifically what's happening.

-Lee
 

knightlie

macrumors 6502a
Feb 18, 2008
546
0
If I later try and access a key from this dictionary from outside EXSomeObject itself, does the whole dictionary get reloaded elsewhere?

If I understand your question correctly then no, it doesn't - someObject points to a dictionary, and if you pass the value of someObject elsewhere then it will still point to the same dictionary. Unless you say

Code:
EXSomeObject *newobject = [someObject copy]

which produces a copy of the object someObject points to then you'll be working with the same EXSomeObject.

Take some time to read up on the difference between objects and pointers to objects, that's what this is about.
 

Byzanti

macrumors newbie
Original poster
Jun 6, 2008
26
2
Thanks to both of you for your help! knightlie, the second part was exactly what I was wondering about.
 

Sydde

macrumors 68030
Aug 17, 2009
2,552
7,050
IOKWARDI
Code:
EXSomeObject *newobject = [someObject copy]

which produces a copy of the object someObject points to then you'll be working with the same EXSomeObject.

Except, not always. The default -copyWithZone: method does allocate a new, retained object, but some classes, like NSDictionary, NSArray and NSString use copy to merely increment the retain count and return the same pointer, because these objects are immutable so physically copying them would be a waste of time. This, of course, does not apply to their mutable subclasses.
 

Byzanti

macrumors newbie
Original poster
Jun 6, 2008
26
2
I've read up (again!) on pointers, and what has been said above, but there is evidentially something I don't get.

In the following code, when I build and analyse it informs me that there is a 'potential leak of an object allocated on line X'. However, from reading the above, and from what I have read, I should not be reallocating an object, I should be merely pointing to it?

The code (simplified):

WindowController.h
Code:
@interface MyWindowController : NSWindowController {
     MySomeObject *someObject;
}

-(void)someMethod;

@end

WindowController.m
Code:
@implementation
-(id) init {
     if (self = [super init]) {
           someObject = [[MySomeObject alloc] init];
           //this persists until dealloc
     }
    return self;
}

-(void)someMethod {
     //The memory leak is supposedly in the line below
     if ([[someObject someArray] count] == 0) do something;
     else if ([[someObject someArray] count] > 0) do something else;
}
@end

MySomeObject.h
Code:
@interface CRBrains : NSObject {
     NSArray *someArray;
}
@property (copy) NSArray *someArray;
-(void)setupSomeArray:(NSArray *)twoArrays;
@end

MySomeObject.m
Code:
@implementation
@synthesize someArray;

//This array comes from a NSOperation
-(void)setupSomeArray:(NSArray *)twoArrays {
	[self setSomeArray:[twoArrays objectAtIndex:0]];
        //There's another array set too, but nm. Both internal arrays and the 'twoArrays' array itself are initialised with class methods, so autoreleased.
}
@end

So, to reiterate, my understanding is that in (void)someMethod the [someObject someArray] isn't being assigned, it's just being pointed to. Why should the build and analyse tool flag this up as a potential leak of the object?

Many thanks
 

lee1210

macrumors 68040
Jan 10, 2005
3,182
3
Dallas, TX
It says it's leaking because it leaks. You said someArray should create a copy on access. You make a copy, you pass count to it, and you let the pointer drift into the ether. You can never release the object you have ownership of. Leak time. No more copy, then retain if you need the returned value around.

-Lee
 

knightlie

macrumors 6502a
Feb 18, 2008
546
0
Yes, change the (copy) to (retain) (or even readonly if you will never change the array from outside the object). Also, you should probably say:

Code:
-(void)someMethod {
     int arrayCount = [someObject.someArray count];

     if (arrayCount == 0) do something;
     else if (arrayCount > 0) do something else;
}

for two reasons:

1. It's a tiny bit more efficient to only call the count method once, and bad form to call it twice in succession like that. Why call it twicen when you know the result won't have changed?
2. Saying someObject.someArray to access a property lets the runtime fetch the property a little more efficiently than [someObject someArray]
 

chown33

Moderator
Staff member
Aug 9, 2009
10,751
8,423
A sea of green
2. Saying someObject.someArray to access a property lets the runtime fetch the property a little more efficiently than [someObject someArray]

This is false. The first form is transformed by the compiler into exactly the same result (a method call, message send) as the second form. There is no difference at all.

Reference:
http://developer.apple.com/library/...html#//apple_ref/doc/uid/TP30001163-CH11-SW17

Code:
myInstance.value = 10;
printf("myInstance value: %d", myInstance.value);
When used with objects, however, dot syntax acts as “syntactic sugar”—it is transformed by the compiler into an invocation of an accessor method. Dot syntax does not directly get or set an instance variable. The code example above is exactly equivalent to the following:
Code:
[myInstance setValue:10];
printf("myInstance value: %d", [myInstance value]);
(underline added for emphasis)
 

Sydde

macrumors 68030
Aug 17, 2009
2,552
7,050
IOKWARDI
Or you could just write a specific method for what you need
Code:
- (NSInteger)someArraysCount { return [someArray count]; }
if you really need to use the copy attribute.
 

Byzanti

macrumors newbie
Original poster
Jun 6, 2008
26
2
Thank you very much for the help so far, everyone. I've read a lot about pointers, copying, retaining and memory management, and things are much clearer now. I also do have the code working with great with (retain), as has been mentioned. If someone could help my clear up one last piece of confusion, I would be very grateful.

My problem is with pointers and @property (copy). My understanding is that if I continue to have my someArray set and got by @property (copy) it should be returning me a copy of the current someArray with a retain count of 1.

In my method above I now have:
Code:
-(void)someMethod {
      NSArray *myArray = [someObject someArray];
      //some other stuff that uses myArray
}

Now, this works, except it is still flagged down as a memory leak. I presume this is because @property (copy) is not returning an autoreleased object.

So, to fix it, I tried these two options:

Code:
-(void)someMethod {
      NSArray *myArray = [[someObject someArray] autorelease];
      //some other stuff that uses myArray
}

-(void)someMethod {
      NSArray *myArray = [someObject someArray];
      //some other stuff that uses myArray
      [myArray release];
}

The thing is, I actually call this method a few times. In between each call, someObject sets someArray to a new array (using [self someArray:[someautoreleased array]]).

Both of the methods above work the first time I run them, however they crash the app the second time I run them. I understand that app crashes are because the app is trying to access some memory which has already been released. What I don't understand is why it should be trying to access already released memory.

Surely by placing NSArray *myArray = [someObject someArray]; at the start of the method, I am getting the latest copy of someArray each time? It should no longer be pointing to the old copy? Indeed, the old copy should no longer exist (having been released), nor be called? But, as the app crashes, I assume I'm either not getting the new copy of someArray, or that myArray is still pointing to the old someArray for some other reason?

Thanks!
 

Byzanti

macrumors newbie
Original poster
Jun 6, 2008
26
2
Actually, I take back that I had it working when using (retain) rather than (copy).

In both cases theses lines comes up as a potential object leak:
NSArray *myArray = [someObject someArray];
NSArray *myArray = [NSArray arrayWithArray:[someObject someArray]];

Similarly this line crashes it the second time it's run:
NSArray *myArray = [[someObject someArray] autorelease];
(or with [myArray release], or indeed [myArray autorelease]).

I don't understand this. How do I get it to not leak and not crash?
 

lee1210

macrumors 68040
Jan 10, 2005
3,182
3
Dallas, TX
You're not showing us all your code so we can't identify all of your problems. Start by showing all your code and telling us exactly what happens when you run it.

You said you read about memory management, which is good, but it still seems like you're hit or miss on ownership. Don't release something you don't own. Don't take ownership of something you don't plan to relinquish ownership of with a release or autorelease.

A lot of people struggle with this, so stick with it. Break things down to the smallest amount that demonstrates your problem that we can run, and post it.

-Lee
 

Sydde

macrumors 68030
Aug 17, 2009
2,552
7,050
IOKWARDI
If you are not worried about thread safety, the (copy) attribute is rarely needed. Personally, I would set the attribute to (retain) and let the caller handle copying. That way you know how the caller will be affecting the lifespan of an object. For instance, if the array is set to (retain) and you are certain another thread will not modify it while you are using it,
Code:
- (void)someMethod {
     NSArray *myArray = [[someObject someArray] [COLOR="Blue"]retain[/COLOR]]; // make sure the array will be alive as long as we need it
     /* use myArray as needed */
     [myArray release]; // release as soon as myArray is no longer needed
     /* maybe do some other stuff*/
}

If thread safety is a serious concern (which you have not mentioned), that someArray might change while you are working on it, just replace the "retain" with "copy".
 

Byzanti

macrumors newbie
Original poster
Jun 6, 2008
26
2
Lee, you're quite right. I've spent hours and hours on this and I still can't figure it out. Code below. Either the code works right and I leak the object, or I don't leak the object, but it crashes the second time I run the methods.

Sydde, thank you for your explanation. Can be sure the error isn't in that bit now.

So, here is my actual code (or the relevant bits - I'll upload the whole damn thing if the error's not in here).

There are three main classes used in this. The first is a WindowController CRMainWindows, the second is a model CRBrains where I store all the arrays and dictionaries. The third is CRArticleParser which is an NSOperation, and updates the stuff held in CRBrains.

So, in order, this happens:

[code removed]

If I have the autorelease above (or a plain [release]) I don't get a leak error, but I get an EXC_BAD_ACCESS the second time I run it.

If I don't have the autorelease (or any other) above, then the app runs fine, but I'm told I'm leaking memory.

Many thanks!
 
Last edited:

knightlie

macrumors 6502a
Feb 18, 2008
546
0
I haven't run the project yet but I think you need to replace all your (copy) properties with (retain) ones, as Sydde says. It looks to me as if you're ending up with many copies of objects where you want to be dealing with the same one. Is there any reason you're using copy so much?
 

Byzanti

macrumors newbie
Original poster
Jun 6, 2008
26
2
I've tried all of them on copy, all of them retain, and then mixtures of the two based what I've read. It doesn't seem to make any difference to the problem I'm getting here.

But, the rules I am following are:
* If it's an immutable object with a mutable subclass, it's copy.
* Other objects are retain (or assign for primitives)
* (Not sure about the logic of this one): NSOperations run on a different thread, so if being initially set from the main thread, I copy them.

I've read that (copy) only copies if it needs to, otherwise it retains. Also following this: http://jakeboxer.com/blog/2011/10/03/my-pre-arc-objective-c-memory-management-conventions/
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.