Pointer Question

Discussion in 'iOS Programming' started by larswik, Feb 25, 2013.

  1. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #1
    I had a problem this weekend with removing items from a NSMutableDict and reloading items to it. The count was 0 after the reload. I created a image to help better explain. I think my problem is with pointers.

    To make things easier to get to I create a new Array in step 2 and go through the mainChracterDict to get to the itemsArray. The users adds and subtracts things from this array and then presses a button to save the current state to a plist and reload data. This replaces the items in the mainCharDict and reloads (refreshes) the mainCharDict.

    But when I go to refresh the itemsArray I first say removeAllObjects so I won't add duplicates to the array in other indexes and then do step 4 by reassigning the refreshed items. But my count is now 0 in the itemsArray?

    I think because I am working with pointers I am removing the items from the original array since my second array (itemsArray) is just pointing to the objects already in memory? So when I reload the count is 0.
     

    Attached Files:

  2. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
  3. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #3
    I'm not really following your description of what you're doing.

    Some points:

    For any object, if you just say

    NSArray *array = [dictionary objectForKey @"someKey"];

    Then "array" will point to the exact same array that's in the dictionary. If you then remove elements from array, you will be removing them from the array that is part of the dictionary.

    In order to not do that, you would need to use:


    NSArray *array = [[dictionary objectForKey @"someKey"] mutableCopy];

    That works because NSArray conforms to the MutableCopying protocol.

    Next point: mutableCopy does a shallow copy. It only copies the top-level object. in the example above, you would get a new mutable array, but the objects in the array would be shared with the original array. If one of those objects was a mutable string, say, and you changed that entry of your new array, the change would also appear in the element of the array that's in the dictionary.

    Next point: Writing a "graph" of objects to a plist and reading it back converts EVERY SINGLE OBJECT to its immutable form. This is a huge pain in the butt sometimes.

    You can write a mutableDeepCopy method that walks an object graph recursively from trunk to leaves, converting every object that has a mutable form to mutable, but it's a non-trivial bit of code to write. (It's confusing.)
     
  4. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #4
    Thanks. I think I found part of my problem. If I have 'array2 = array1' array2 is not pointing to the objects in array1, it is array2 is pointing to array1's pointer which is pointing to the object in memory.

    I am rewriting to code to make this simpler so I can't post it but I think I get what is happening and causing my problems.

    Thanks for the explanation on shallow copy and deep copy. As for reading from a plist, if I read that into an NSMutableDictionary I would still need to make a mutableCopy of that, I don't understand why since I brought the data in to a NSMutDict?
     
  5. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #5
    No, that's incorrect. If you have:
    Code:
    NSArray *array1 = [[NSArray alloc] init];
    NSArray *array2 = array1;
    array1 and array2 are now pointers to the same memory location (and, effectively, the object in memory).

    Try it yourself: add this code and set a breakpoint after it; you will see in the Debug Area - Variables View that both array1 and array2 have been assigned the same value (memory address).
     
  6. larswik, Feb 27, 2013
    Last edited by a moderator: Feb 27, 2013

    larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #6
    Thanks dejo, I have not had time to test it the last couple of nights but I should be able to tonight when I get home.

    But since they are pointed to the same memory location I could then say
    Code:
    [array2 removeAllObjects];
    NSLog@"array1 has %d objects";
    The result would be that array1 would have a 0 count, even though I removed the objects from array2 since they both point to the same location in memory, yes?
     
  7. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #7
    If they were mutable, yes. If they are immutable, no, since they have no removeAllObjects method.
     
  8. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #8
    You are right about the results, but are thinking about it wrong.

    array1 and array2 are 2 different local variables. Those 2 local variables both point to the same object.

    So, when you execute the line


    Code:
     [array2 removeAllObjects];
    
    You are removing all objects from the array that the variable array2 points to. Since the array1 variable points to the same object, you see the same results no matter which variable you look at.

    It's like you have 2 different phone numbers that ring on the same librarian's desk. Let's call the librarian Bob. You call the first phone number and tell the person that answers: "shred all your card catalogs. You have 5 minutes."

    Then, 5 minutes later, you call the second phone number and ask the person who answers: "how many cards do you have total in your card catalogs?". Bob says "None. You just told me to shred them, and I had to scramble to get it done in the 5 minutes you gave me!"

    Both phone numbers (variables) talk to the same librarian (object).

    Now, if you did this:

    Code:
     NSArray *array2 = [array1 mutableCopy];
    
    Then you hired a second librarian, Susan, photocopied all Bob's cards, and gave those copies to Susan. Now, if you tell array1 to remove all it's objects, it has no effect on array2, since array2 is a different array.

    There is a gotcha, however. The ARRAY (card catalog) is copied, but the objects in the array (books on the shelf) are not. Both arrays point to the same objects (both card catalogs refer to the same books.)

    What happens if somebody looks up a book in card catalog A, checks out the book, tears out half the pages, and returns the book? Wwhen somebody else looks up the book in card catalog B and checks out the book, they get a book with half it's pages missing, even though they looked up the book in a different card catalog.

    The same holds for arrays. If you change the data in one of the objects in array1, array2 points to the same object, so when you fetch the object, you see the changes even though you made the change to an object you fetched in array1.

    Many of the things you put in arrays are immutable. If the object is immutable, it doesn't matter that both arrays hold the same objects. The objects can't change.

    It gets worse if one of the objects in the array is a mutable container like another array, a dictionary, or a set.

    Unless you do a recursive deep copy, duplicating every single object in both arrays, you can have shared objects between the arrays and changes to array1 can have side effects on array2.
     
  9. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #9
    That makes total scenes and I get that. 2 pointers that point to the same object. Like when I bought my new car with 2 car keys. So when the wife uses the car she changes the seat position, radio station. Then when I access the car those settings have been changed.

    So that explains my original problem with the image I showed up top. I tried to create a shortcut to an array that was deep inside an NSMutableDict. So when I removed the objects from myShortCutArray it removed the objects from the array that was in the NSMutableDict.

    It sounds like if I want to do it this way I need to make a mutableCopy of those objects to work with in a new array. When I am finished working with the copy then just replace the array in my NSMutableDict with the copy that I was working with.

    Thanks again guys!
     

Share This Page