Pointer Logic and NSArray, NSData

Discussion in 'Mac Programming' started by MorphingDragon, Jun 9, 2010.

  1. MorphingDragon macrumors 603

    MorphingDragon

    Joined:
    Mar 27, 2009
    Location:
    The World Inbetween
    #1
    Does an array or dataset store pointers or actual objects?

    If it is a pointer,

    then, say

    array1 = array2;

    Would that make array1 point to array2? Or would it make array1 point to the objects that array2 points to?

    So if you went:

    Code:
    array1 = array2;
    array2 = nil;
    
    Therefore array1 would also be equal to nil, or will array1 point to the objects that array2 used to point to?

    I ask this as

    Code:
    array1 = [NSMutableArray arrayWithArray: array2];
    
    Leaks ridiculous amounts of memory.
     
  2. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #2
    Think of pointers just like numbers.

    int array2 = 5;
    int array1 = array2;
    array2 = 0;

    What is the value of array1? Well its 5 still.

    +arrayWithArray: (NSArray *)array
    Does two things, it makes a NEW array (or mutableArray) with pointers to all the objects in the original. And it sends retain to all those objects.

    If you are done with the first array, you still need to release it. Once the first array's retaincount goes to 0, it will release all the objects inside of it.
     
  3. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #3
    In Objective-C the only way you can get to an Object is through a pointer. So:
    Code:
    NSArray *myArrayOne = [NSArray arrayWithObjects: @"One",@"Two",@"Three",nil];
    NSArray *myArrayTwo = myArrayOne;
    myArrayOne = nil;
    
    Results in an NSArray Object being created "out there", and a pointer to it being returned and assigned to the variable myArrayOne, whose type is NSArray *. The second line will take the pointer and store it to the variable myArrayTwo. There is only one NSArray Object in play "out there", but now two things are storing the memory address where it is stored. The third line will set myArrayOne to point to nil, the "nothing pointer". myArrayTwo still holds the address to the NSArray Object that's "out there". You could set it to nil, too, but you'd no longer have any way to get to your NSArray Object. It would still sit there happily, storing its Objects, with a dumb grin on its face. Since you don't own the Object created with arrayWithObjects, when the autorelease pool gets drained it will go away.

    For all intents and purposes you need to think of pointers as your way to get to an Object, *not* the Object itself. You can make copies of the pointer, and act on the Object through both pointers, but it's the same Object out there that's being affected.

    In your example:
    array1 = array2;

    Assuming these are pointers to some Object, it just stores the pointer that array2 was holding in array1. The Object is unaffected.

    array1 = array2;
    array2 = nil;

    array1 will have the pointer to the Object, array2 will be nil and point to no Object anymore. The Object itself will remain unaffected.

    Code:
    NSArray *array1 = [NSMutableArray arrayWithArray: array2];
    This doesn't leak any memory at all. You simply have two arrays. array1 will be an autoreleased Object, so unless you retain it, then fail to release it, no leaking will go on at all. If you owned the Object pointed to by array2, failed to release it, then set array2 to nil with no other way to get to that Object, you might have problems.

    Code:
    NSArray *array2 = [[NSArray alloc] initWithObjects: @"One",@"Two",@"Three",nil];
    NSArray *array1 = [NSMutableArray arrayWithArray: array2]; //I left this as a pass to NSMutableArray because that's what you had
    array2 = nil;
    
    *This* leaks. array2 was pointing to an NSArray that you had ownership of, then before releasing it, you lost your only pointer. Now you're in trouble.

    As a separate note, arrayWithArray passed to NSMutableArray will return a pointer to an NSArray, NOT an NSMutableArray. If you have an NSArray and you want a mutable version you can use setArray: or addObjectsFromArray:.

    Code:
    NSArray *array2 = [[NSArray alloc] initWithObjects: @"One",@"Two",@"Three",nil];
    NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:[array2 count]];
    [array1 addObjectsFromArray: array2];
    [array2 release];
    array2 = nil;
    
    This is a contrived example, but this will get you an NSArray you own, then get a mutable copy and assign the pointer to array1, then relinquish ownership of the Object pointed to by array2, then set array2 to nil.

    -Lee
     
  4. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #4
    Sorry, Lee, that is not correct.

    [NSMutableArray arrayWithArray:someArray]

    is equivalent to

    [[someArray mutableCopy] autorelease]

    (edit)

    More precisely, most of the +objectWithContent:source

    convenience methods can be expanded to

    [[[class alloc] initWithContent:source] autorelease]

    assuming there is a matching -initWithContent: method.
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    Hm. Thanks for the correction. I couldn't find this method documented in the NSMutableArray documentation so I thought it was inherited from NSArray. Is there some documentation about this expansion you describe, or is it just the "normal" way things are without formal codification?

    -Lee

    edit: by your explanation shouldn't it be mutableArrayWithArray:, then?
     
  6. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #6
    Since there is no actual +mutableArrayWithArray: method, the distinction is more-or-less pedantic, the class method is inherited and perhaps overridden. In the documentation section on "The Objective-C Language 2.0" under "Allocating and Initialzing Objects" I saw mention of this in the subsections "Constraints and Conventions" and "Combining Allocation and Initialization" (referencing each other).

    Basically, the convenience methods for creating and initializing an object can be thought of as encapsulating the +alloc for that class, so if you use the immutableClass method on the mutableSubClass, you can expect the latter as the returned object, just as if you used -initWithArray: on an alloc'd mutable array, you will get a mutable array because that is what you allocated.

    I discovered this sort of on non-accident. I used the convenience methods to create mutable objects and I got mutable objects. If it works in one class cluster, it pretty much has to work in all of them, otherwise you are introducing dangerous inconsistency into the system.
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    Well, good times all around. I feel like it's a bug in the docs if NSMutableArray overrides arrayWithArray: without mention, but people are probably getting what they are hoping for when calling it. I'd say that since it's not documented as returning an NSMutableArray you *should* be getting NSArray's implementation that returns an immutable object going strictly by the docs. In this case it sounds like the docs should just be fixed to match the behavior.

    I hope I can't be blamed for believing the docs =).

    -Lee
     
  8. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #8
    Honestly, I believe the behavior as it stands. The methods that belong to a class ( both + and - ) should pertain to that class. If a class inherits a method from a superclass, it should implement that method, by over-riding it if necessary, in the context of the subclass. For a mutable object to use a method inherited from an immutable superclass, it really should return a mutable object, because the method is being applied to a mutable object.

    The way to distinguish is by the method declaration. For instance,

    + (id)stringWithCString: (const char *)cString

    will return a mutable string when used as an inherited method on NSMutableString. OTOH,

    + (NSString *)pathWithComponents: (NSArray)components

    will not. You can see that right in the declaration. If you look at the section "Constraints and Conventions" mentioned above, you will see the text that makes this behavior obvious and how to tell what methods to trust. In fact, even the compiler should warn you (assuming the right warning flags) when you might be making a mistake.
     
  9. MorphingDragon thread starter macrumors 603

    MorphingDragon

    Joined:
    Mar 27, 2009
    Location:
    The World Inbetween

Share This Page