Last active
January 9, 2024 14:38
-
-
Save robb/d55b72d62d32deaee5fa to your computer and use it in GitHub Desktop.
A macro to convert nullable references to nonnull references while triggering an assert if the expression is actually true. Think of this as unsafe unwrap for Objective-C.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NS_ASSUME_NONNULL_BEGIN | |
void Log(NSString *foo) { | |
NSLog(@"%@", foo); | |
} | |
int main(int argc, const char * argv[]) { | |
@autoreleasepool { | |
NSDictionary *stuff = @{ | |
@"a": @"Test" | |
}; | |
// This will trigger a warning in Xcode 7 if | |
// `-Wnullable-to-nonnull-conversion` is enabled: | |
// | |
// Implicit conversion from nullable pointer 'id _Nullable' to non- | |
// nullable pointer type 'NSString * _Nonnull'. | |
Log(stuff[@"a"]); | |
/// This will not trigger a warning. | |
Log(RBBNotNil(stuff[@"a"])); | |
/// This will trigger the assert. | |
Log(RBBNotNil(stuff[@"b"])); | |
} | |
return 0; | |
} | |
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if __has_feature(objc_generics) | |
/// An unimplemented class used to trick the compiler, since a cast along the | |
/// lines of | |
/// | |
/// (__nonnull __typeof(bla))bla; | |
/// | |
/// is not possible. | |
@interface RBBBox<__covariant Type> | |
- (nonnull Type)asNonNull; | |
@end | |
/// This macro allows us to cast a nullable reference to a non-nullable | |
/// reference that would otherwise trigger a warning if | |
/// `-Wnullable-to-nonnull-conversion` is enabled. | |
#define RBBNotNil(V) \ | |
({ \ | |
NSCAssert(V, @"Expected '%@' not to be nil.", @#V); \ | |
RBBBox<__typeof(V)> *type; \ | |
(__typeof(type.asNonNull))V; \ | |
}) | |
#else | |
/// If generics are unavailable, so is `-Wnullable-to-nonnull-conversion`. | |
#define RBBNotNil(V) \ | |
({ \ | |
NSCAssert(V, @"Expected '%@' not to be nil.", @#V); \ | |
V; \ | |
}) | |
#endif |
#define assumeNotNull(_value) \ ({ if (!_value) abort(); __auto_type const _temp = _value; _temp; })
Unfortunately broke with clang 15 update (Xcode 14.3 and later) but that's because the clang devs now inherit nullability as an attribute, which is quite annoying as they don't treat it as a standard qualifier. E.g. in clang 16 they don't drop it when you use typeof_unqual()
instead of typeof()
@CodingMarkus, can we use typeof_unqual
instead?
#define assumeNotNull(_value) \
({ if (!_value) abort(); typeof_unqual(_value) _Nonnull const _temp = _value; _temp; })
@CodingMarkus, can we use
typeof_unqual
instead?
Not unless they've changed the behavior in the meantime, just as I said:
which is quite annoying as they don't treat it as a standard qualifier.
E.g. in clang 16 they don't drop it when you usetypeof_unqual()
instead oftypeof()
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
use as