Last active
October 29, 2018 11:10
-
-
Save McDevon/6d0008f45d06747acec30e8e00e0f819 to your computer and use it in GitHub Desktop.
First working attempt on property observers with 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
#import "NSObject+PropertyObservers.h" | |
#import <objc/runtime.h> | |
@implementation NSObject (PropertyObservers) | |
+ (void)enablePropertyObservers | |
{ | |
uint count; | |
Class class = [self class]; | |
objc_property_t* properties = class_copyPropertyList(class, &count); | |
for (int i = 0; i < count; ++i) | |
{ | |
const char* propertyName = property_getName(properties[i]); | |
NSString *nameString = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]; | |
const char *attributes = property_getAttributes(properties[i]); | |
NSString *attributeData = [NSString stringWithCString:attributes encoding:NSUTF8StringEncoding]; | |
NSArray *attributeArray = [attributeData componentsSeparatedByString:@","]; | |
if ([attributeArray containsObject:@"R"]) { | |
continue; | |
} | |
const char* getterName = property_copyAttributeValue( properties[i], "G" ); | |
const char* setterName = property_copyAttributeValue( properties[i], "S" ); | |
if (getterName != NULL || setterName != NULL) { | |
NSLog(@"Cannot create property observers for %@. It has renamed setter or getter.", nameString); | |
} | |
NSString *capitalizedPropertyName = [[[nameString substringToIndex:1] uppercaseString] stringByAppendingString:[nameString substringFromIndex:1]]; | |
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@:", capitalizedPropertyName]); | |
NSString *willSetMethodName = [NSString stringWithFormat:@"willSet%@:oldValue:", capitalizedPropertyName]; | |
NSString *didSetMethodName = [NSString stringWithFormat:@"didSet%@:oldValue:", capitalizedPropertyName]; | |
BOOL willSetImplemented = [class instancesRespondToSelector:NSSelectorFromString(willSetMethodName)]; | |
BOOL didSetImplemented = [class instancesRespondToSelector:NSSelectorFromString(didSetMethodName)]; | |
SEL poSelector; | |
if (willSetImplemented && didSetImplemented) { | |
poSelector = @selector(po_set_id_willset_didset:); | |
} else if (willSetImplemented) { | |
poSelector = @selector(po_set_id_willset:); | |
} else if (didSetImplemented) { | |
poSelector = @selector(po_set_id_didset:); | |
} else { | |
continue; | |
} | |
SEL newSetterSelector = NSSelectorFromString([NSString stringWithFormat:@"po_replacement_set%@:", capitalizedPropertyName]); | |
Method originalMethod = class_getInstanceMethod(class, setter); | |
Method poMethod = class_getInstanceMethod(class, poSelector); | |
BOOL didAddReplacementSetter = class_addMethod(class, | |
newSetterSelector, | |
method_getImplementation(originalMethod), | |
method_getTypeEncoding(originalMethod)); | |
BOOL didAddPoSetter = class_replaceMethod(class, | |
setter, | |
method_getImplementation(poMethod), | |
method_getTypeEncoding(poMethod)); | |
if (!didAddPoSetter || !didAddReplacementSetter) { | |
NSLog(@"*ERROR %d %d", didAddPoSetter, didAddReplacementSetter); | |
} | |
} | |
} | |
- (void)po_set_id_willset_didset:(id)value | |
{ | |
NSString *tempProp = [NSStringFromSelector(_cmd) substringFromIndex:3]; | |
NSString * const propertyName = [tempProp substringToIndex:tempProp.length - 1]; | |
SEL getterSel = NSSelectorFromString([[[propertyName substringToIndex:1] lowercaseString] stringByAppendingString:[propertyName substringFromIndex:1]]); | |
SEL setterSel = NSSelectorFromString([NSString stringWithFormat:@"po_replacement_set%@:", propertyName]); | |
SEL willSetSel = NSSelectorFromString([NSString stringWithFormat:@"willSet%@:oldValue:", propertyName]); | |
SEL didSetSel = NSSelectorFromString([NSString stringWithFormat:@"didSet%@:oldValue:", propertyName]); | |
IMP getterImp = [self methodForSelector:getterSel]; | |
id (*getter)(id, SEL) = (void *)getterImp; | |
id oldValue = getter(self, getterSel); | |
IMP willSetImp = [self methodForSelector:willSetSel]; | |
void (*willSet)(id, SEL, id ,id) = (void *)willSetImp; | |
willSet(self, willSetSel, value, oldValue); | |
IMP setterImp = [self methodForSelector:setterSel]; | |
void (*setter)(id, SEL, id) = (void *)setterImp; | |
setter(self, setterSel, value); | |
IMP didSetImp = [self methodForSelector:didSetSel]; | |
void (*didSet)(id, SEL, id ,id) = (void *)didSetImp; | |
didSet(self, didSetSel, value, oldValue); | |
} | |
- (void)po_set_id_didset:(id)value | |
{ | |
NSString *propertyName = [NSStringFromSelector(_cmd) substringFromIndex:3]; | |
propertyName = [propertyName substringToIndex:propertyName.length - 1]; | |
SEL getterSel = NSSelectorFromString([[[propertyName substringToIndex:1] lowercaseString] stringByAppendingString:[propertyName substringFromIndex:1]]); | |
SEL setterSel = NSSelectorFromString([NSString stringWithFormat:@"po_replacement_set%@:", propertyName]); | |
SEL didSetSel = NSSelectorFromString([NSString stringWithFormat:@"didSet%@:oldValue:", propertyName]); | |
IMP getterImp = [self methodForSelector:getterSel]; | |
id (*getter)(id, SEL) = (void *)getterImp; | |
id oldValue = getter(self, getterSel); | |
IMP setterImp = [self methodForSelector:setterSel]; | |
void (*setter)(id, SEL, id) = (void *)setterImp; | |
setter(self, setterSel, value); | |
IMP didSetImp = [self methodForSelector:didSetSel]; | |
void (*didSet)(id, SEL, id ,id) = (void *)didSetImp; | |
didSet(self, didSetSel, value, oldValue); | |
} | |
- (void)po_set_id_willset:(id)value | |
{ | |
NSString *propertyName = [NSStringFromSelector(_cmd) substringFromIndex:3]; | |
propertyName = [propertyName substringToIndex:propertyName.length - 1]; | |
SEL getterSel = NSSelectorFromString([[[propertyName substringToIndex:1] lowercaseString] stringByAppendingString:[propertyName substringFromIndex:1]]); | |
SEL setterSel = NSSelectorFromString([NSString stringWithFormat:@"po_replacement_set%@:", propertyName]); | |
SEL willSetSel = NSSelectorFromString([NSString stringWithFormat:@"willSet%@:oldValue:", propertyName]); | |
IMP getterImp = [self methodForSelector:getterSel]; | |
id (*getter)(id, SEL) = (void *)getterImp; | |
id oldValue = getter(self, getterSel); | |
IMP willSetImp = [self methodForSelector:willSetSel]; | |
void (*willSet)(id, SEL, id ,id) = (void *)willSetImp; | |
willSet(self, willSetSel, value, oldValue); | |
IMP setterImp = [self methodForSelector:setterSel]; | |
void (*setter)(id, SEL, id) = (void *)setterImp; | |
setter(self, setterSel, value); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment