Skip to content

Instantly share code, notes, and snippets.

@robb
Last active January 9, 2024 14:38
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robb/d55b72d62d32deaee5fa to your computer and use it in GitHub Desktop.
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.
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
#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
@moshe-foreflight
Copy link

@CodingMarkus, can we use typeof_unqual instead?

#define assumeNotNull(_value) \
    ({ if (!_value) abort(); typeof_unqual(_value) _Nonnull const _temp = _value; _temp; })

@CodingMarkus
Copy link

@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 use typeof_unqual() instead of typeof()

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