Created
September 11, 2017 11:30
-
-
Save trenskow/b9397f3ec8c4f87bab84024891398803 to your computer and use it in GitHub Desktop.
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
// | |
// NSObject+NSObject_AssociatedObjects.h | |
// Created by Kristian Trenskow. | |
// | |
#import <Foundation/Foundation.h> | |
/** | |
Associated objects additions to `NSObject`. | |
*/ | |
@interface NSObject (AssociatedObjects) | |
/** | |
Returns an `NSMutableDictionary` instance with associated objects. | |
*/ | |
@property (nonatomic,readonly,nonnull) NSMutableDictionary<NSString *, id> *associatedObjects; | |
@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
// | |
// NSObject+AssociatedObjects.m | |
// Created by Kristian Trenskow. | |
// | |
#import <objc/runtime.h> | |
#import "NSObject+AssociatedObjects.h" | |
unsigned int NSObjectAssociatedObjectsKey = 0; | |
@implementation NSObject (AssociatedObjects) | |
- (NSMutableDictionary<NSString *,id> *)associatedObjects { | |
// If no associated object has been set - we create one, set it and return it. | |
NSMutableDictionary<NSString *, id> *associatedObjects = objc_getAssociatedObject(self, &NSObjectAssociatedObjectsKey); | |
if (!associatedObjects) { | |
associatedObjects = [NSMutableDictionary new]; | |
objc_setAssociatedObject(self, &NSObjectAssociatedObjectsKey, associatedObjects, OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
} | |
return associatedObjects; | |
} | |
@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
// | |
// NSObject+Properties.h | |
// Created by Kristian Trenskow. | |
// | |
#import <Foundation/Foundation.h> | |
/** | |
A class representing runtime information a property. | |
*/ | |
@interface NSPropertyInfo : NSObject | |
/** | |
Returns the properties name. | |
*/ | |
@property (nonatomic,readonly,nonnull) NSString *name; | |
/** | |
Returns `YES` if property is read-only. | |
*/ | |
@property (nonatomic,readonly,getter=isReadOnly) BOOL readOnly; | |
/** | |
Returns `YES` if property's value is copied on set. | |
*/ | |
@property (nonatomic,readonly,getter=isCopy) BOOL copy; | |
/** | |
Returns `YES` if property's value is holds a strong reference. | |
*/ | |
@property (nonatomic,readonly,getter=isStrong) BOOL strong; | |
/** | |
Returns `YES` if property is atomic. | |
*/ | |
@property (nonatomic,readonly,getter=isAtomic) BOOL atomic; | |
/** | |
Returns the selector for the getter. | |
*/ | |
@property (nonatomic,readonly,nonnull) SEL getter; | |
/** | |
Returns the selector for the setter. | |
*/ | |
@property (nonatomic,readonly,nonnull) SEL setter; | |
/** | |
Returns `YES` if the property's implementation is dynamic - added at runtime. | |
*/ | |
@property (nonatomic,readonly,getter=isDynamic) BOOL dynamic; | |
/** | |
Returns `YES` if the property holds a weak reference. | |
*/ | |
@property (nonatomic,readonly,getter=isWeak) BOOL weak; | |
/** | |
Return `YES` if the property can be garbage collected (Mac OS X only). | |
*/ | |
@property (nonatomic,readonly,getter=isGarbageCollectable) BOOL garbageCollectable; | |
/** | |
Returns the type encoding for the property. | |
*/ | |
@property (nonatomic,readonly,nonnull) NSString *typeEncoding; | |
/** | |
When `typeEncoding` is `@` it returns the `Class` of the property - otherwise nil. | |
@note This is only non-nil if the information is available at runtime. | |
*/ | |
@property (nonatomic,readonly,nullable) Class type; | |
@end | |
/** | |
Properties additions to `NSObject`. | |
*/ | |
@interface NSObject (Properties) | |
/** | |
Returns an `NSDictionary` instance where the key is the property name and the value is an `NSPropertyInfo` object with property information. | |
*/ | |
@property (nonatomic,readonly,nonnull) NSDictionary<NSString *, NSPropertyInfo *> *properties; | |
@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
// | |
// NSObject+Properties.m | |
// Created by Kristian Trenskow. | |
// | |
#import <objc/runtime.h> | |
#import "NSObject+AssociatedObjects.h" | |
#import "NSObject+Properties.h" | |
@interface NSPropertyInfo () | |
@property (nonatomic,readwrite,nonnull) NSString *name; | |
@property (nonatomic,readwrite,getter=isReadOnly) BOOL readOnly; | |
@property (nonatomic,readwrite,getter=isCopy) BOOL copy; | |
@property (nonatomic,readwrite,getter=isStrong) BOOL strong; | |
@property (nonatomic,readwrite,getter=isAtomic) BOOL atomic; | |
@property (nonatomic,readwrite,nonnull) SEL getter; | |
@property (nonatomic,readwrite,nonnull) SEL setter; | |
@property (nonatomic,readwrite,getter=isDynamic) BOOL dynamic; | |
@property (nonatomic,readwrite,getter=isWeak) BOOL weak; | |
@property (nonatomic,readwrite,getter=isGarbageCollectable) BOOL garbageCollectable; | |
@property (nonatomic,readwrite,nonnull) NSString *typeEncoding; | |
@property (nonatomic,readwrite,nullable) Class type; | |
@end | |
NSString * const NSPropertiesKey = @"NSPropertiesKey"; | |
@implementation NSPropertyInfo | |
- (instancetype)initWithProperty:(objc_property_t)property { | |
if ((self = [super init])) { | |
_name = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; | |
// ARC default property attributes. | |
_strong = YES; | |
_atomic = YES; | |
_getter = NSSelectorFromString(_name); | |
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@", | |
[[_name substringWithRange:NSMakeRange(0, 1)] uppercaseString], | |
[_name substringFromIndex:1]]); | |
NSArray<NSString *> *attributes = [[NSString stringWithCString:property_getAttributes(property) | |
encoding:NSUTF8StringEncoding] | |
componentsSeparatedByString:@","]; | |
for (NSString *attribute in attributes) { | |
switch ([attribute characterAtIndex:0]) { | |
case 'R': | |
_readOnly = YES; | |
break; | |
case 'C': | |
_copy = YES; | |
break; | |
case '&': | |
_strong = YES; | |
break; | |
case 'N': | |
_atomic = NO; | |
break; | |
case 'G': | |
_getter = NSSelectorFromString([attribute substringFromIndex:1]); | |
break; | |
case 'S': | |
_setter = NSSelectorFromString([attribute substringFromIndex:1]); | |
break; | |
case 'D': | |
_dynamic = YES; | |
break; | |
case 'W': | |
_weak = YES; | |
break; | |
case 'P': | |
_garbageCollectable = YES; | |
break; | |
case 'T': | |
_typeEncoding = [attribute substringFromIndex:1]; | |
if ([_typeEncoding characterAtIndex:0] == '@' && [_typeEncoding length] > 1) { | |
_type = NSClassFromString([_typeEncoding substringWithRange:NSMakeRange(2, [_typeEncoding length] - 3)]); | |
_typeEncoding = @"@"; | |
} | |
break; | |
} | |
} | |
} | |
return self; | |
} | |
@end | |
@implementation NSObject (Properties) | |
- (NSDictionary<NSString *,NSObjectPropertyInfo *> *)properties { | |
NSDictionary<NSString *, NSObjectPropertyInfo *> *ret = self.associatedObjects[NSObjectPropertiesKey]; | |
if (!ret) { | |
NSMutableDictionary<NSString *, NSObjectPropertyInfo *> *infos = [NSMutableDictionary new]; | |
Class currentClass = [self class]; | |
while (currentClass != [NSObject class]) { | |
unsigned int count; | |
objc_property_t *properties = class_copyPropertyList(currentClass, &count); | |
for (unsigned int idx = 0 ; idx < count ; idx++) { | |
NSObjectPropertyInfo *propertyInfo = [[NSObjectPropertyInfo alloc] initWithProperty:properties[idx]]; | |
infos[propertyInfo.name] = propertyInfo; | |
} | |
currentClass = [currentClass superclass]; | |
free(properties); | |
} | |
ret = self.associatedObjects[NSObjectPropertiesKey] = [infos copy]; | |
} | |
return ret; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I know it's really a "no no!" to name things in the NS namespace - but this is just an example - so just rename if you don't like it.