0

When I run this code inside a call made from SenTest's STAssertThrowsSpecificNamed:

@throw [[NSException alloc] initWithName:NSInvalidArchiveOperationException
                                  reason:@"---some reason----"
                               userInfo:nil];

I get (with NSZombieEnabled=YES):

*** -[NSException reason]: message sent to deallocated instance 0x100a81d60

The exception is somehow deallocated before STAssertThrowsSpecificNamed has finished processing it.

I can avoid the error by replacing the @throw line above with this code:

NSException *exception = [NSException exceptionWithName:NSInvalidArchiveOperationException
                                                 reason:@"---some reason----"
                                               userInfo:nil];
@throw exception;

I get exactly the same behavior with or without ARC. Without ARC this code avoids the error too:

@throw [[[NSException alloc] initWithName:NSInvalidArchiveOperationException
                                   reason:@"---some reason----"
                                userInfo:nil] retain];

Is this a bug in SenTest? Or a bug in the compiler? Or is my first @throw just incorrect?

paulmelnikow
  • 16,895
  • 8
  • 63
  • 114

3 Answers3

2

@throw releases the object after it's done using it, so use -retain if you want to include it on the same line as @throw.

@throw [[[[NSException alloc] initWithName:NSInvalidArchiveOperationException
                                  reason:@"---some reason----"
                               userInfo:nil] retain] autorelease];

This should do the trick.

EDIT: To check for ARC-specific code, use:

if(__has_feature(objc_arc)) {
    @throw [[[NSException alloc] initWithName:NSInvalidArchiveOperationException
                                       reason:@"---some reason----"
                                     userInfo:nil];
} else {
    @throw [[[[NSException alloc] initWithName:NSInvalidArchiveOperationException
                                      reason:@"---some reason----"
                                   userInfo:nil] retain] autorelease];
}
Aditya Vaidyam
  • 6,259
  • 3
  • 24
  • 26
  • And is the second example what you recommend under ARC, where I can't use `retain`? – paulmelnikow Mar 06 '12 at 16:32
  • The reason this would work is because I see that @throw releases the NSExceptions you create, so if you pass a -retain/-autorelease NSException to it, it will retain, and release when necessary. Retain counts are supposed to be used this way when "giving an object to another object", and with -autorelease, it just means "released if necessary, or leave it the garbage collection". – Aditya Vaidyam Mar 06 '12 at 17:16
  • Under ARC, you might have to define it as __strong (if I remember correctly, since I try hard to avoid ARC). In ARC, retain counts are indirectly handled by the modern runtime, and weak and strong typing tells it what to do with the object. – Aditya Vaidyam Mar 06 '12 at 17:17
  • I need a solution that works for each, so for now I'm using the second code block from my original post: `id exc = [NSException exceptionWithName:....]; @throw exc;`. – paulmelnikow Mar 07 '12 at 16:48
  • It still strikes me as odd that `@throw` is intentionally designed this way. – paulmelnikow Mar 07 '12 at 16:49
  • You need to change `if(__has_feature...` to `#if __has_feature(` for the above code to compile under ARC, but it still doesn't work (see original post). – paulmelnikow Mar 08 '12 at 19:17
1

I'm now exclusively using +[NSException raise:format:] under both ARC and manual retain–release.

paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
1

I'm sticking with this form, for now, which seems to work, both with and without ARC.

id exc = [NSException exceptionWithName:NSInvalidArchiveOperationException
                                 reason:@"---some reason----"
                               userInfo:nil];
@throw exc;

Per Galaxas0's answer, @throw is designed to release the exception after it processes it. This still strikes me as odd, especially under ARC.

In a non-ARC project, use this (also per Galaxas0):

@throw [[[[NSException alloc] initWithName:NSInvalidArchiveOperationException
                                    reason:@"---some reason----"
                                  userInfo:nil] retain] autorelease];
paulmelnikow
  • 16,895
  • 8
  • 63
  • 114