-1

I have following code

NSArray *myArray = [[NSArray alloc] initWithObjects:@"1",@"2",nil];
NSMutableDictionary *dic0 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag", @"name", myArray, @"arraye",nil];
NSMutableDictionary *dic1 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag1", @"name", myArray, @"arrayr",nil];
NSMutableDictionary *dic2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayq",nil];
NSMutableDictionary *dic3 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayqe",nil];
NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3];

NSLog(@"array12424 %d", [array12424 retainCount]);
NSLog(@"array12424 %@", array12424);
int j = [myArray retainCount];
for(int i=0;i<j; ++i)
{
    [myArray release];
        NSLog(@"%d", i);
    NSLog(@"myArray %@", myArray);
}

NSLog(@"array12424 %@", array12424);  

After releasing myArray,i expected crash; why arraye and other array key are pointing to array12424?

//output  

  2012-02-02 12:33:58.454 212121212[6924:a0f] array12424 1
    2012-02-02 12:33:58.459 212121212[6924:a0f] array12424 (
            {
            arraye =         (
                1,
                2
            );
            name = parag;
        },
            {
            arrayr =         (
                1,
                2
            );
            name = parag1;
        },
            {
            arrayq =         (
                1,
                2
            );
            name = parag2;
        },
            {
            arrayqe =         (
                1,
                2
            );
            name = parag2;
        }
    )
    2012-02-02 12:33:58.459 212121212[6924:a0f] 0
    2012-02-02 12:33:58.460 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.460 212121212[6924:a0f] 1
    2012-02-02 12:33:58.460 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.461 212121212[6924:a0f] 2
    2012-02-02 12:33:58.462 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.463 212121212[6924:a0f] 3
    2012-02-02 12:33:58.463 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.464 212121212[6924:a0f] 4
    2012-02-02 12:33:58.466 212121212[6924:a0f] myArray myArray 
    2012-02-02 12:33:58.466 212121212[6924:a0f] array12424 (
            {
            arraye = "array12424 ";
            name = parag;
        },
            {
            arrayr = "array12424 ";
            name = parag1;
        },
            {
            arrayq = "array12424 ";
            name = parag2;
        },
            {
            arrayqe = "array12424 ";
            name = parag2;
        }
    )

I am using MAC osx 10.6 base SDK.

Parag Bafna
  • 22,812
  • 8
  • 71
  • 144

5 Answers5

4

0) Do not use retainCount.

1) Cocoa Collections retain their contents, then release them when they are removed, or when the collection is destroyed. Furthermore there's no documentation which explicitly states the APIs you call through will not autorelease their arguments.

2) You don't initialize your array correctly -- it must be nil terminated: NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3];

3) Simply put, it does not matter -- It's Undefined Behavior. The result cannot be predicted (unless it involves raptors). It crashes in other OS versions. It crashes as you'd expect with GuardMalloc enabled. It crashes shortly afterwards (provided you're lucky).

justin
  • 104,054
  • 14
  • 179
  • 226
  • I know Cocoa Collections retain their contents. thats why retainCount of myArray is 5. but i am releasing myArray 5 times. – Parag Bafna Feb 02 '12 at 09:59
  • This surely doesn't address the question? The code appears to release, and maybe eventually over-release an array and the array reference appears to end up referring to a string. The use of `retainCount` doesn't explain that. The behaviour of collections doesn't explain that (a collection can retain all it likes, if the code does a release it could balance the collection's retain). – CRD Feb 02 '12 at 10:18
1

Take 2, let's take you through the code with more detail. First we need to fix the 3 warnings, and beef up the logging:

+ (void) test
{
   NSArray *myArray = [[NSArray alloc] initWithObjects:@"1",@"2",nil];
   NSMutableDictionary *dic0 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag", @"name", myArray, @"arraye",nil];
   NSMutableDictionary *dic1 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag1", @"name", myArray, @"arrayr",nil];
   NSMutableDictionary *dic2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayq",nil];
   NSMutableDictionary *dic3 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayqe",nil];
   NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3, nil];

   NSLog(@"array12424 %lu", [array12424 retainCount]);
   NSLog(@"array12424 %p, %@", array12424, array12424);
   NSUInteger j = [myArray retainCount];
   for(NSUInteger i=0;i<j; ++i)
   {
      NSLog(@"%lu, %lx", i, [myArray retainCount]);
      [myArray release];
      NSLog(@"%lu, %lx", i, [myArray retainCount]);
      NSLog(@"myArray %p, %@", myArray, myArray);           // breakpoint here
   }

   NSLog(@"array12424 %p, %@", array12424, array12424);  
}

Put a breakpoint on the indicated line and call that method.

When you hit the breakpoint the first time you'll see the following on the console:

2012-02-03 07:36:34.651 sandpit[32581:903] array12424 1
2012-02-03 07:36:34.659 sandpit[32581:903] array12424 0x100176820, (
        {
        arraye =         (
            1,
            2
        );
        name = parag;
    },
        {
        arrayr =         (
            1,
            2
        );
        name = parag1;
    },
        {
        arrayq =         (
            1,
            2
        );
        name = parag2;
    },
        {
        arrayqe =         (
            1,
            2
        );
        name = parag2;
    }
)
2012-02-03 07:36:34.660 sandpit[32581:903] 0, 5
2012-02-03 07:36:34.661 sandpit[32581:903] 0, 4

Looking at the last put one line first we see the retain count is 5. Now we created and own the array - that's 1 - and we've added it to 4 dictionaries and they retain their arguments - that's the other 4. Could any of that 5 actually be misleading, could there be a pending autorelease or two around which will make it drop? Well in this case we can determine not as we've not autoreleased and collections retain their arguments.

In the debugger variable display look at the entry for myArray

myArray = (NSCFArray *) 0x100513aa0 2 objects

This tells us that the Obj-C object referenced my myArray is indeed an array and its located at memory address 0x100513aa0 and contains two items. You'll see that address in the console as a result of adding '%p' to some of the NSLog's.

Now press continue a few times until the last console output is:

2012-02-03 07:37:33.074 sandpit[32581:903] 4, 1
2012-02-03 07:37:33.074 sandpit[32581:903] 4, fffffffffffffff

Now you can see why I used `%lx' to print out the retain count rather than '%lu' which matches its type (unsigned 64-bit integer). The retain count has apparently gone negative!

What has really happened is the immediately preceding release sent the count to 0 and then the memory became free for re-use - and all those NSLog's are formatting strings and require memory so there is some churn going on.

Now look at the myArray entry in the debugger - does it still say it is an array with 2 objects in it? Probably not. I've run this code in various guises and it has thought it is an empty array, an array with 2 nulls, a string... and what did you see in the question - strings in the NSLog output... It's pointing to freed memory, it could contain anything.

There is NO memory safety in Obj-C, you can read and write all over memory as much as you like. There is no guarantee that doing something like over-releasing memory will result in an immediate crash, or indeed any crash at all, but the application will most likely malfunction in some way.

HTH.

If you want to dig deeper you could write your own code to print the dictionaries displaying the addresses (using '%p') of the contained objects, or just dig into the dictionaries in the debugger, so you can see the sharing.

And the obligatory health warning: retainCount can be helpful while learning but never, ever, use it in real code to determine whether something needs to be released as there may be pending decrements (from autoreleases) you don't know about.

CRD
  • 52,522
  • 5
  • 70
  • 86
  • 1
    retainCount is not useful. Not even for learning. The absolute value doesn't reflect outstanding autoreleases, is meaningless in the context of threads, and may change significantly from platform to platform and from release to release, including minor releases. Other than that last paragraph, great answer! – bbum Feb 02 '12 at 20:36
  • @bbum - you missed the para after the second code snippet, pure heresy surely, and the *apparently* negative comments... Assisted learning here. Cars kill millions, encourage overpopulation, damage the environment, start wars, etc. etc. problems galore; but are they *never* useful? Let's agree to disagree on this one. – CRD Feb 02 '12 at 21:07
  • 1
    `retainCount` does not assist with learning. It may seem like it in the short term, but then the developer has to unlearn everything they thought they learned through `retainCount` as soon as they start on a real project. It is a waste of time as has been proven over and over again in the last 20 years that I've been writing Objective-C code, teaching developers, and working on the tools. One of the big reasons why we explicitly removed `retainCount` from ARC was quite specifically because it is useless. – bbum Feb 02 '12 at 21:45
  • @bbum - did you re-read that para, and the last line of your first comment? I too have many years behind me (and yes I've designed and implemented concurrent memory management systems), that's not the point. Let's agree to disagree on this one, Parag Bafna must be regretting asking for help! – CRD Feb 02 '12 at 22:18
0

First add nil at this statement

NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3,nil];

now coming to your crash just change this code

for(int i=0;i<j; ++i)
{
    [myArray release];
    NSLog(@"%d", i);
    NSLog(@"let the system remove this array, give him some time dude to remove it, then see what happens");
    NSLog(@"myArray %@", myArray);
}
Inder Kumar Rathore
  • 39,458
  • 17
  • 135
  • 184
-1

I am assuming this question is to understand cocoa internal working ...

retain count 0 does not mean object get deleted ... cocoa internally has its own logic to call dealloc on the object after retain count reaches to 0.

you can try calling last statement in different event context ... normally object use to be get deleted after its event context.

Girish Kolari
  • 2,515
  • 2
  • 24
  • 34
  • 1
    Not *after* retain count reaches 0. In fact, the retain count will never reach zero. There's no reason for it to; if you send `release` to an object with a retain count of 1, it will simply deallocate itself. There's no point in updating state that can never be read. – Peter Hosey Feb 02 '12 at 07:36
  • Also, “event context”s only matter when an object has been autoreleased (and only in an application, where NSApplication or UIApplication is creating and re-creating a pool for you every event). You can't ever assume an object hasn't been autoreleased, but if you haven't autoreleased it yourself, you can't assume it has been, either. Assume it will die when you release your last ownership of it. – Peter Hosey Feb 02 '12 at 07:38
  • release -- document states "The receiver is sent a dealloc message when its reference count reaches 0" "it never say it releases ". concept of ownership ship never recommend you to release others owner ship -- for more information on event context "just try it will crash" – Girish Kolari Feb 02 '12 at 07:52
  • The behavior is undefined, therefore it may not crash. – bbum Feb 02 '12 at 20:33
-1

My guess is you are trying to understand memory allocation & deallocation and this isn't code you intend to put in a real application.

In that case try exploring by printing object references with the %p format - this will print the address of the object. You will then be able to see what objects are really shared (rather than just have the same contents) and when memory gets reused, etc. Or use the debugger to give you the same thing.

The general advice to avoid retainCount can be waived will while exploring.

CRD
  • 52,522
  • 5
  • 70
  • 86
  • 1
    *The general advice to avoid retainCount can be waived will while exploring.* Sorry -- it *cannot*. There's way too much implementation that's abstracted within objc libraries to use this as proof. When manipulating a container, what methods will use release, and which will use autorelease? Determining that is more complicated than just learning proper ref counting, IMO. If you need to drop to that level CoreFoundation is predictable *if* you don't use any autorelease pools and break on any "No Autorelease Pool" errors. – justin Feb 02 '12 at 07:35
  • @Justin - maybe I should have said "bent". It's a public method, it can show you that the libraries are retaining stuff, etc. It's not that `retainCount` gives you the wrong answer but that it can sometimes be misleading due to pending autoreleases, but by showing the count hasn't gone down after an autorelease the observer learns. Surely the advice is aimed against making assumptions, not against learning? So when exploring, go explore! And a good idea might be to wrap some of your code into a local autorelease pool and check the retainCount before & after exiting it to see the effect it has. – CRD Feb 02 '12 at 10:11
  • except, it's just not that simple. – justin Feb 02 '12 at 11:41
  • 1
    And you can learn to drive a screw with a hammer, but you'd still be learning to do it wrong. `retainCount` is only useful for learning that the implementation details of the frameworks make `retainCount` useless. – bbum Feb 02 '12 at 20:34
  • And yet the memory manager *relies* on this *useless* retain count. Let's agree to disagree on this one folks. – CRD Feb 02 '12 at 21:30
  • Yes, the retain count is an *internal implementation detail* that the runtime *uses internally* to manage memory. That does not contribute to the meaning of the absolute value of the retain count. In fact, that is the primary reason why it is so useless; you can't know what the value means without also knowing every last detail about the implementation of every API through which an object reference passes. – bbum Feb 02 '12 at 21:47
  • @CRD My longer respose to "except…" (which I deleted) shared the same sentiment as the second half of bbum's 1st resopnse. If one wanted to understand why it's just not that simple, I'd recommend these approaches: Use breakpoints in overrides of release, retain, and autorelease. Use Instruments to record reference counts (it will include the backtraces). Test in multiple systems and system versions, under multiple reference counting schemes (e.g. MRC, ARC, GC). You'll find a lot of energy has been put into their designs, and that the ref counting contract is all you can and should rely on. – justin Feb 02 '12 at 21:55
  • @Justin - let's agree to disagree. See thread on other answer. – CRD Feb 02 '12 at 22:58
  • @CRD alright, let's. dropped. fwiw, i had seen your second answer earlier today. – justin Feb 02 '12 at 23:05