1

As far as I know str1 is released after assignment. I just receive warning but why don't it crash?

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    UIViewController * vc = [[UIViewController alloc] init] ;
    self.window.rootViewController = vc ;
    [self.window makeKeyAndVisible];

    NSString * __unsafe_unretained str1 = [[NSString alloc] initWithFormat:@"First Name: %@", @"cc"] ;
    // I think it should crash here !
    NSLog(@"string: %u", [str1 length]) ;

    NSString * __weak str2 = [[NSString alloc] initWithFormat:@"First Name: %@", @"cc"] ;
    // nil for str2
    NSLog(@"string: %@", str2) ;

    return YES;
}

It may be clear if I modify the code like this.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    UIViewController * vc = [[UIViewController alloc] init] ;
    self.window.rootViewController = vc ;
    [self.window makeKeyAndVisible];

    NSString * __unsafe_unretained str1 = [[NSString alloc] initWithFormat:@"First Name: %@", @"cc"] ;
    // I think it should crash here !
    NSLog(@"string: %u", [str1 length]) ;

    TestObject * __unsafe_unretained obj1 = [[TestObject alloc] init] ;
    NSLog(@"obj1:%@", obj1) ;

    return YES;
}

TestObject.m:

@implementation TestObject

- (void)dealloc
{
    NSLog(@"%@, %@", NSStringFromSelector(_cmd), self) ;
}

@end

The log is

2013-12-09 15:49:08.625 xxx[23950:a0b] string: 14
2013-12-09 15:49:08.627 xxx[23950:a0b] dealloc, <TestObject: 0x8970ff0>
2013-12-09 15:49:08.627 xxx[23950:a0b] obj1:<TestObject: 0x8970ff0>
KudoCC
  • 6,912
  • 1
  • 24
  • 53
  • are you using `str1` outside the local pool / method? – Anoop Vaidya Dec 09 '13 at 07:16
  • @AnoopVaidya No, I just use it to test arc and study about it. – KudoCC Dec 09 '13 at 07:17
  • It is considered an error to use, but the memory will not be deallocated until the end of the run loop, when the autorelease pool drains. (This behavior may be different with code optimization enabled.) – Aaron Brager Dec 09 '13 at 07:19
  • It will crash if use that string afterwords...like `NSString *str2=[NSString stringWithFormat:@"%@",str1];` – Preetam Jadakar Dec 09 '13 at 07:21
  • @preetam Yes, you are right, but why? – KudoCC Dec 09 '13 at 07:24
  • @AaronBrager You mean str1 is marked autoreleased? As @preetam said, it will carsh if use it like `NSString *str2=[NSString stringWithFormat:@"%@",str1];`, so I don't think so. – KudoCC Dec 09 '13 at 07:25

1 Answers1

3

There are two possible things happening there.

Aaron Brager covered one of them - if an object is in an autorelease pool, the pool doesn't drain until the next cycle of the run loop so the object will be granted a temporary reprieve. However ARC is able to elide many instances of autorelease and is doing so in this case.

So the answer is that the memory has not yet been overwritten so your calls appear to work fine. But that's just an accident. If we reorder things a bit we get a very different result:

    NSString * __unsafe_unretained str1 = [[NSString alloc] initWithFormat:@"First Name: %@", @"cc"];

    // I think it should crash here !

    TestObject * __unsafe_unretained obj1 = [[TestObject alloc] init];
    NSLog(@"obj1:%@", obj1);

    //oops, crash!
    NSLog(@"string: %u", [str1 length]);

In this case, the memory pointed to by str1 was overwritten by the allocated TestObject, so the first pointer-sized byte was changed from pointing at NSString's isa to TestClass' isa. That means asking for length will fail. But obj1 is just as invalid as str1... That memory is in the free list now, just waiting to be re-used. In actual fact it contains random junk, it just so happens that the junk "appears" to be a valid Objective-C object until it is reused.

If you add a length method to TestClass, my example will appear to work again! Don't be deceived... The C standard says that accessing an invalid pointer is undefined behavior, meaning the compiler is free to format your hard drive, delete all your contacts, or do anything at all. It can even do this before the first instruction in your program is executed because a program that contains undefined operations is entirely undefined. (It is not defined right up until the moment it does something crazy, it is undefined in its entirety).

So the short version is: You performed an undefined operation (accessing a free'd pointer), and rather than formatting your hard drive, the compiler decided to pretend your program made sense. That is merely an artifact of the compiler's optimizations and how memory happened to be used, but the outcome is just as meaningless as printing "Hippo" and exiting. (Or more practically, run with the Zombies instrument and/or guard malloc enabled and you will find your original code example crashes immediately).

russbishop
  • 16,587
  • 7
  • 61
  • 74