Skip to content

Instantly share code, notes, and snippets.

@nicked
Last active May 22, 2020 06:56
Show Gist options
  • Save nicked/94bafcfd90c29a58345688bd163901f6 to your computer and use it in GitHub Desktop.
Save nicked/94bafcfd90c29a58345688bd163901f6 to your computer and use it in GitHub Desktop.
Wrapper for Objective-C runtime property functions
@import Foundation;
@import ObjectiveC.runtime;
typedef NS_ENUM(char, SetterType) {
Assign, Strong, Weak, Copy
};
@interface ClassProperty : NSObject
@property (nonatomic, readonly) NSString *name;
/// This will be the raw type encoding for now
@property (nonatomic, readonly) NSString *encodeType;
@property (nonatomic, readonly) BOOL isReadOnly;
@property (nonatomic, readonly) BOOL isNonAtomic;
@property (nonatomic, readonly) BOOL isDynamic;
@property (nonatomic, readonly) SetterType setterType;
/// Custom getter or the default getter
@property (nonatomic, readonly) SEL getter;
/// Custom or default setter, NULL if the property is readonly
@property (nonatomic, readonly, nullable) SEL setter;
/// Will be nil if the property is computed or dynamic
@property (nonatomic, readonly, nullable) NSString *ivarName;
- (instancetype)initWithProperty:(objc_property_t)property;
@end
@implementation ClassProperty
@synthesize getter = _getter;
@synthesize setter = _setter;
- (instancetype)initWithProperty:(objc_property_t)property {
self = [super init];
if (self) {
_name = @(property_getName(property));
_setterType = Assign; // this is the default
NSString *attribStr = @(property_getAttributes(property));
NSArray *attribs = [attribStr componentsSeparatedByString:@","];
for (NSString *attrib in attribs) {
unichar attribChar = [attrib characterAtIndex:0];
NSString *attribDetail = [attrib substringFromIndex:1];
switch (attribChar) {
case 'T':
_encodeType = attribDetail;
break;
case 'R':
_isReadOnly = YES;
break;
case 'N':
_isNonAtomic = YES;
break;
case 'D':
_isDynamic = YES;
break;
case '&':
_setterType = Strong;
break;
case 'W':
_setterType = Weak;
break;
case 'C':
_setterType = Copy;
break;
case 'G':
_getter = NSSelectorFromString(attribDetail);
break;
case 'S':
_setter = NSSelectorFromString(attribDetail);
break;
case 'V':
_ivarName = attribDetail;
break;
default:
NSAssert(NO, @"Unknown attribute: %@", attrib);
}
}
if (_getter == NULL) {
_getter = NSSelectorFromString(_name);
}
if (_setter == NULL && _isReadOnly == NO) {
NSString *name = [NSString stringWithFormat:@"set%@%@:",
[_name substringToIndex:1].uppercaseString,
[_name substringFromIndex:1]];
_setter = NSSelectorFromString(name);
}
}
return self;
}
@end
@interface NSObject (Introspection)
@property (class, readonly) NSArray<ClassProperty *> *classProperties;
@end
@implementation NSObject (Introspection)
+ (NSArray<ClassProperty *> *)classProperties {
NSMutableArray *clsProps = [NSMutableArray array];
uint propCount;
objc_property_t *props = class_copyPropertyList(self.class, &propCount);
for (uint n = 0; n < propCount; n++) {
[clsProps addObject:[[ClassProperty alloc] initWithProperty:props[n]]];
}
free(props);
return clsProps;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment