Skip to content

Instantly share code, notes, and snippets.

@McDevon
Last active October 29, 2018 11:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save McDevon/6d0008f45d06747acec30e8e00e0f819 to your computer and use it in GitHub Desktop.
Save McDevon/6d0008f45d06747acec30e8e00e0f819 to your computer and use it in GitHub Desktop.
First working attempt on property observers with Objective-C
#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