Register FAQ / Rules Forum Spy Search Today's Posts Mark Forums Read
Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old May 14, 2012, 07:38 PM   #1
Byzanti
macrumors newbie
 
Join Date: Jun 2008
Obj-C: instance variables from another object

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:@"someKey"]);

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

Many thanks!

Last edited by Byzanti; May 14, 2012 at 07:45 PM.
Byzanti is offline   0 Reply With Quote
Old May 14, 2012, 08:06 PM   #2
lee1210
macrumors 68040
 
lee1210's Avatar
 
Join Date: Jan 2005
Location: 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
lee1210 is offline   0 Reply With Quote
Old May 15, 2012, 01:56 AM   #3
knightlie
macrumors 6502a
 
Join Date: Feb 2008
Quote:
Originally Posted by Byzanti View Post
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.
__________________
2011 Mac Mini i5 | iPad Mini | Wife | Cat
knightlie is offline   0 Reply With Quote
Old May 15, 2012, 05:19 AM   #4
Byzanti
Thread Starter
macrumors newbie
 
Join Date: Jun 2008
Thanks to both of you for your help! knightlie, the second part was exactly what I was wondering about.
Byzanti is offline   0 Reply With Quote
Old May 15, 2012, 11:27 AM   #5
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
Quote:
Originally Posted by knightlie View Post
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.
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old May 16, 2012, 06:38 PM   #6
Byzanti
Thread Starter
macrumors newbie
 
Join Date: Jun 2008
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
Byzanti is offline   0 Reply With Quote
Old May 16, 2012, 07:32 PM   #7
lee1210
macrumors 68040
 
lee1210's Avatar
 
Join Date: Jan 2005
Location: 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
lee1210 is offline   1 Reply With Quote
Old May 17, 2012, 03:03 AM   #8
knightlie
macrumors 6502a
 
Join Date: Feb 2008
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]
__________________
2011 Mac Mini i5 | iPad Mini | Wife | Cat
knightlie is offline   0 Reply With Quote
Old May 17, 2012, 09:49 AM   #9
chown33
macrumors 603
 
Join Date: Aug 2009
Quote:
Originally Posted by knightlie View Post
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/m...1163-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)
chown33 is offline   1 Reply With Quote
Old May 17, 2012, 02:28 PM   #10
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
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.
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old May 17, 2012, 04:08 PM   #11
Byzanti
Thread Starter
macrumors newbie
 
Join Date: Jun 2008
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 is offline   0 Reply With Quote
Old May 17, 2012, 05:30 PM   #12
Byzanti
Thread Starter
macrumors newbie
 
Join Date: Jun 2008
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?
Byzanti is offline   0 Reply With Quote
Old May 17, 2012, 07:05 PM   #13
lee1210
macrumors 68040
 
lee1210's Avatar
 
Join Date: Jan 2005
Location: 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
lee1210 is offline   1 Reply With Quote
Old May 17, 2012, 11:54 PM   #14
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
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] retain]; // 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".
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old May 18, 2012, 10:10 AM   #15
Byzanti
Thread Starter
macrumors newbie
 
Join Date: Jun 2008
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 by Byzanti; Apr 1, 2013 at 08:58 AM.
Byzanti is offline   0 Reply With Quote
Old May 18, 2012, 12:20 PM   #16
knightlie
macrumors 6502a
 
Join Date: Feb 2008
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?
__________________
2011 Mac Mini i5 | iPad Mini | Wife | Cat
knightlie is offline   0 Reply With Quote
Old May 18, 2012, 01:09 PM   #17
Byzanti
Thread Starter
macrumors newbie
 
Join Date: Jun 2008
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...t-conventions/
Byzanti is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 09:11 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC