Skip to content

Instantly share code, notes, and snippets.

@wearhere
Created August 29, 2012 00:33
Show Gist options
  • Save wearhere/3505652 to your computer and use it in GitHub Desktop.
Save wearhere/3505652 to your computer and use it in GitHub Desktop.
Boolean literals are only sometimes of type BOOL.
- (void)testBooleanLiteralsAreOfTypeBool {
NSNumber *testBoolNumber = @YES;
const char *typeString = [testBoolNumber objCType];
STAssertTrue('c' == *typeString, @"Boxed BOOL is of type %s, not of type 'c' (signed char, aka BOOL).", typeString);
}
- (void)testBoxedBooleanLiteralsAreOfTypeBool {
// Boxing works fine if a BOOL value is boxed directly...
BOOL testBool = YES;
NSNumber *testBoolNumber = @(testBool);
const char *typeString = [testBoolNumber objCType];
STAssertTrue('c' == *typeString, @"Boxed BOOL is of type %s, not of type 'c' (signed char, aka BOOL).", typeString);
// ""
BOOL testBool2 = !testBool;
NSNumber *testBoolNumber2 = @(testBool2);
const char *typeString2 = [testBoolNumber2 objCType];
STAssertTrue('c' == *typeString2, @"Boxed BOOL is of type %s, not of type 'c' (signed char, aka BOOL).", typeString2);
// ""
NSNumber *testBoolNumber3 = [self testBoxedBooleanLiteralsAreOfTypeBoolMethodBoxingABoolLArgument:testBool];
const char *typeString3 = [testBoolNumber3 objCType];
STAssertTrue('c' == *typeString3, @"Boxed BOOL is of type %s, not of type 'c' (signed char, aka BOOL).", typeString3);
// ""
NSNumber *testBoolNumber4 = @([testBoolNumber3 boolValue]);
const char *typeString4 = [testBoolNumber4 objCType];
STAssertTrue('c' == *typeString4, @"Boxed BOOL is of type '%s', not of type 'c' (signed char, aka BOOL).", typeString4);
// .. but not if a "raw Boolean" is boxed.
// Note: YES is here typedef'd to __objc_yes.
NSNumber *testBoolNumber5 = @(YES);
const char *typeString5 = [testBoolNumber5 objCType];
STAssertFalse('c' == *typeString5, @"This is not broken, as expected: Boxed BOOL is of type 'c' (signed char, aka BOOL), not of type 'i'.");
// ... ditto the result of a "boolean expression".
NSNumber *testBoolNumber6 = @(!testBool);
const char *typeString6 = [testBoolNumber6 objCType];
STAssertFalse('c' == *typeString6, @"This is not broken, as expected: Boxed BOOL is of type 'c' (signed char, aka BOOL), not of type 'i'.");
// If we cast the failing cases to BOOLs prior to boxing, we're alright.
NSNumber *testBoolNumber7 = @((BOOL)YES);
const char *typeString7 = [testBoolNumber7 objCType];
STAssertTrue('c' == *typeString7, @"Boxed BOOL is of type %s, not of type 'c' (signed char, aka BOOL).", typeString7);
NSNumber *testBoolNumber8 = @((BOOL)!testBool);
const char *typeString8 = [testBoolNumber8 objCType];
STAssertTrue('c' == *typeString8, @"Boxed BOOL is of type %s, not of type 'c' (signed char, aka BOOL).", typeString8);
}
- (NSNumber *)testBoxedBooleanLiteralsAreOfTypeBoolMethodBoxingABoolLArgument:(BOOL)testBool {
return @(testBool);
}
@wearhere
Copy link
Author

NB: Error cases are indicated with STAssertFalse.

@wearhere
Copy link
Author

Full explanation: in cases 5 and 6, the NSNumbers produced do not have objCType == 'c', but rather 'i'. This implies that the compiler is generating +[NSNumber numberWithInt:] from the literal syntax in cases 5 and 6, not +[NSNumber numberWithBOOL:] like in the others.

This can be a problem if you're serializing these NSNumbers into JSON, where (depending on your library, at least SBJSON, but likely the others -- they probably all rely upon -[NSNumber objCType] to serialize numeric vs. Boolean values) the Boolean values will be represented by "1" or "0" instead of by "true" and "false".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment