Skip to content

Instantly share code, notes, and snippets.

@JensAyton
Created October 22, 2011 17:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JensAyton/1306226 to your computer and use it in GitHub Desktop.
Save JensAyton/1306226 to your computer and use it in GitHub Desktop.
Fun with ARC and exceptions
/*
The desired output of each test case is:
<MyException 0xPTR>{ TestName, retain count 1 } created.
<MyException 0xPTR>{ TestName, retain count 1 } caught.
<MyException 0xPTR>{ TestName, retain count 1 } dealloced.
For a fun time, try to predict the actual output for all six test cases!
UPDATE: I’ve been pointed at the Clang ARC internals documentation
(http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.exceptions),
which say that the compiler doesn’t clean up after exceptions unless
-fobjc-arc-exceptions is passed. This is not mentioned in the Transitioning
to ARC Release Notes. With the flag, the ConvenienceConstructor variants all
work, and the AllocInit variants all crash.
FURTHER UPDATE: Documentation enhancement request: http://www.openradar.me/10329810
*/
#import <Foundation/Foundation.h>
@interface MyException: NSObject
+ (id) exceptionWithLabel:(NSString *)label;
- (id) initWithLabel:(NSString *)label;
@end
// Test cases
static void DirectThrowConvenienceConstructorTest(NSString *name)
{
@throw [MyException exceptionWithLabel:name];
}
static void DirectThrowAllocInitTest(NSString *name)
{
@throw [[MyException alloc] initWithLabel:name];
}
static void IndirectThrow(id exception) __attribute__((noreturn, noinline));
static void IndirectThrow(id exception)
{
@throw exception;
}
static void IndirectThrowConvenienceConstructorTest(NSString *name)
{
IndirectThrow([MyException exceptionWithLabel:name]);
}
static void IndirectThrowAllocInitTest(NSString *name)
{
IndirectThrow([[MyException alloc] initWithLabel:name]);
}
static void AutoreleasingThrow(__autoreleasing id exception) __attribute__((noreturn, noinline));
static void AutoreleasingThrow(__autoreleasing id exception)
{
@throw exception;
}
static void AutoreleasingThrowConvenienceConstructorTest(NSString *name)
{
AutoreleasingThrow([MyException exceptionWithLabel:name]);
}
static void AutoreleasingThrowAllocInitTest(NSString *name)
{
AutoreleasingThrow([[MyException alloc] initWithLabel:name]);
}
// Test rig
extern void _NSSetLogCStringFunction(void (*)(const char *string, unsigned length, BOOL withSyslogBanner));
static void PrintNSLogMessage(const char *string, unsigned length, BOOL withSyslogBanner)
{
puts(string);
}
static void RunTest_(void (*testCase)(NSString *name), NSString *name)
{
NSLog(@"Running test %@", name);
__weak MyException *weakException;
@autoreleasepool
{
@try
{
testCase(name);
}
@catch (MyException *exception)
{
weakException = exception;
NSLog(@"%@ caught.", exception);
}
}
if (weakException != nil)
{
NSLog(@"%@ appears to have leaked.", weakException);
}
NSLog(@"\n");
}
#define RunTest(NAME) RunTest_(NAME, @#NAME)
int main (int argc, const char * argv[])
{
// Avoid annoying log headers.
_NSSetLogCStringFunction(PrintNSLogMessage);
@autoreleasepool
{
RunTest(IndirectThrowConvenienceConstructorTest);
RunTest(IndirectThrowAllocInitTest);
RunTest(AutoreleasingThrowConvenienceConstructorTest);
RunTest(AutoreleasingThrowAllocInitTest);
RunTest(DirectThrowConvenienceConstructorTest);
RunTest(DirectThrowAllocInitTest);
}
}
@implementation MyException
{
NSString *_label;
}
+ (id) exceptionWithLabel:(NSString *)label
{
id result = [[self alloc] initWithLabel:label];
// Compiling the exception class separately with ARC off made no difference.
#if !__has_extension(objc_arc)
[result autorelease];
#endif
return result;
}
- (id) initWithLabel:(NSString *)label
{
if ((self = [super init]))
{
_label = label;
NSLog(@"%@ created.", self);
}
return self;
}
- (void) dealloc
{
NSLog(@"%@ dealloced.", self);
_label = [NSString stringWithFormat:@"ZOMBIE - was %@", _label];
#if !__has_extension(objc_arc)
[super dealloc];
#endif
}
- (NSString *) description
{
return [NSString stringWithFormat:@"<%@ %p>{ %@, retain count %lu }", self.class, self, _label, CFGetRetainCount((__bridge CFTypeRef)self)];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment