From d631877ebba71176fc338922542ac03e6dc5d5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endika=20Guti=C3=A9rrez=20Salas?= Date: Sun, 8 Sep 2013 20:05:14 +0200 Subject: [PATCH 1/4] Added definition of compact method --- Classes/NSArray+ObjectiveSugar.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Classes/NSArray+ObjectiveSugar.h b/Classes/NSArray+ObjectiveSugar.h index a835513..088c965 100644 --- a/Classes/NSArray+ObjectiveSugar.h +++ b/Classes/NSArray+ObjectiveSugar.h @@ -191,4 +191,11 @@ - (NSArray *)symmetricDifference:(NSArray *)array; +/** + Returns a copy of self with all NSNull elements removed. + + @return A copy of self with all NSNull elements removed. + */ +- (NSArray *)compact; + @end From 0844c35fad062df726fef02bdfa11eb1cd9504e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endika=20Guti=C3=A9rrez=20Salas?= Date: Sun, 8 Sep 2013 20:06:35 +0200 Subject: [PATCH 2/4] Added implementation for NSArray#compact --- Classes/NSArray+ObjectiveSugar.m | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Classes/NSArray+ObjectiveSugar.m b/Classes/NSArray+ObjectiveSugar.m index 07b71df..a1f094b 100644 --- a/Classes/NSArray+ObjectiveSugar.m +++ b/Classes/NSArray+ObjectiveSugar.m @@ -155,6 +155,16 @@ - (NSArray *)reverse { return self.reverseObjectEnumerator.allObjects; } +- (NSArray *)compact { + id resultValues[self.count]; + int count = 0; + for (id el in self) { + if (el != [NSNull null]) + resultValues[count++] = el; + } + return [NSArray arrayWithObjects:resultValues count:count]; +} + #pragma mark - Set operations - (NSArray *)intersectionWithArray:(NSArray *)array { From e52c2ae718a96a7d3bbed297660afd20472a7743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endika=20Guti=C3=A9rrez=20Salas?= Date: Sun, 8 Sep 2013 20:09:52 +0200 Subject: [PATCH 3/4] Dictionary merge methods added. --- Classes/NSDictionary+ObjectiveSugar.h | 3 +++ Classes/NSDictionary+ObjectiveSugar.m | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Classes/NSDictionary+ObjectiveSugar.h b/Classes/NSDictionary+ObjectiveSugar.h index 1dce388..db563a6 100644 --- a/Classes/NSDictionary+ObjectiveSugar.h +++ b/Classes/NSDictionary+ObjectiveSugar.h @@ -10,6 +10,9 @@ @interface NSDictionary (ObjectiveSugar) ++ (NSDictionary *)dictionaryByMerging:(NSDictionary *) dict1 with:(NSDictionary *) dict2; +- (NSDictionary *)dictionaryByMergingWith:(NSDictionary *)dict; + - (void)each:(void (^)(id key, id value))block; - (void)eachKey:(void (^)(id key))block; - (void)eachValue:(void (^)(id value))block; diff --git a/Classes/NSDictionary+ObjectiveSugar.m b/Classes/NSDictionary+ObjectiveSugar.m index 5ea9317..23e0990 100644 --- a/Classes/NSDictionary+ObjectiveSugar.m +++ b/Classes/NSDictionary+ObjectiveSugar.m @@ -10,6 +10,28 @@ @implementation NSDictionary (Rubyfy) ++ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2 +{ + NSMutableDictionary * result = [NSMutableDictionary dictionaryWithDictionary:dict1]; + + [dict2 enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) { + if ([dict1 objectForKey:key]) { + if ([obj isKindOfClass:[NSDictionary class]]) { + NSDictionary * newVal = [[dict1 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj]; + [result setObject: newVal forKey: key]; + } + } else { + [result setObject: obj forKey: key]; + } + }]; + + return (NSDictionary *) result; +} +- (NSDictionary *)dictionaryByMergingWith:(NSDictionary *)dict +{ + return [NSDictionary dictionaryByMerging:self with:dict]; +} + - (void)each:(void (^)(id k, id v))block { [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { block(key, obj); From d336e2d9d24307814144f8e710e0877455ab32a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endika=20Guti=C3=A9rrez=20Salas?= Date: Sun, 8 Sep 2013 20:14:07 +0200 Subject: [PATCH 4/4] Added object additions for metaprogramming --- Classes/NSObject+ObjectiveSugar.h | 73 ++++++++++ Classes/NSObject+ObjectiveSugar.m | 216 ++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+) create mode 100644 Classes/NSObject+ObjectiveSugar.h create mode 100644 Classes/NSObject+ObjectiveSugar.m diff --git a/Classes/NSObject+ObjectiveSugar.h b/Classes/NSObject+ObjectiveSugar.h new file mode 100644 index 0000000..a161011 --- /dev/null +++ b/Classes/NSObject+ObjectiveSugar.h @@ -0,0 +1,73 @@ +// +// NSObject+Additions.h +// TenzingCore +// +// Created by Endika GutiƩrrez Salas on 10/16/12. +// Copyright (c) 2012 Tenzing. All rights reserved. +// + +#import + +#define $(sel) (@selector(sel)) +#define $_(sel) ([NSValue valueWithPointer:(@selector(sel))]) + +@interface NSObject (Additions) + +- (id)initWithValuesInDictionary:(NSDictionary *)dict; + +- (NSDictionary *)asDictionary; + +- (id)trySelector:(SEL)selector; +- (id)trySelector:(SEL)selector withObject:(id)obj; +- (id)trySelector:(SEL)selector withObject:(id)obj0 withObject:(id)obj1; + ++ (void)defineMethod:(SEL)selector do:(id(^)(id _self, ...))implementation; ++ (void)defineClassMethod:(SEL)selector do:(id(^)(id _self, ...))implementation; + ++ (Class)subclass:(NSString *)className; + +/** + Inspects all instance methods for calling class + + @return an array of strings containing all instance methods for calling class + + */ ++ (NSArray *)instanceMethods; + +/** + * Inspects all instance methods for calling class + * + * @return an array of strings containing all instance methods for calling class + */ ++ (NSArray *)classMethods; + +/** + * Inspects all class methods for calling class + * + * @return an array of strings containing all class methods for calling class + */ ++ (NSArray *)instanceProperties; + +/** + * Inspects class of a given property. Checks class of property NOT class of object at property. + * + * @return class of property or nil if class does not has property or if it is not object + */ ++ (Class)classForProperty:(NSString *)propertyName; + +/** + * Inspects type of a given property and returns it's code. + * See https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html + * + * @return char with type encoding + */ ++ (char)typeForProperty:(NSString *)propertyName; + +/** + * Checks if object has property + * + * @return true if object has property with name + */ ++ (BOOL)hasProperty:(NSString *)propertyName; + +@end diff --git a/Classes/NSObject+ObjectiveSugar.m b/Classes/NSObject+ObjectiveSugar.m new file mode 100644 index 0000000..d47fbc9 --- /dev/null +++ b/Classes/NSObject+ObjectiveSugar.m @@ -0,0 +1,216 @@ +// +// NSObject+Additions.m +// TenzingCore +// +// Created by Endika GutiƩrrez Salas on 10/16/12. +// Copyright (c) 2012 Tenzing. All rights reserved. +// + +#import "NSObject+Additions.h" + +#import +#import "NSArray+Additions.h" + +@implementation NSObject (Additions) + +- (id)initWithValuesInDictionary:(NSDictionary *)dict +{ + self = [self init]; + if (self) { + for (NSString *key in dict) { + + @try { + NSObject *value = dict[key]; + + if ([value isKindOfClass:NSNull.class]) { + value = nil; + + // Encode recursively objects + } else if ([value isKindOfClass:[NSDictionary class]]) { + Class class = [self.class classForProperty:key]; + if (class && class != [NSDictionary class]) { + value = [[class alloc] initWithValuesInDictionary:(NSDictionary *) value]; + } + } else if ([value isKindOfClass:[NSArray class]]) { + Class class = [self trySelector:NSSelectorFromString([NSString stringWithFormat:@"%@Class", key])]; + if (class && class != [NSArray class]) { + value = [(NSArray *) value transform:^id(id obj) { + return [obj isKindOfClass:[NSDictionary class]] + ? [[class alloc] initWithValuesInDictionary:obj] + : obj; + }]; + } + } + [self setValue:value forKey:key]; + } + @catch (NSException *exception) { + // Do nothing + } + + } + } + return self; +} + +- (NSDictionary *)asDictionary +{ + NSArray *properties = [self.class instanceProperties]; + + return [properties map:^id(NSString *property) { + id value = [self valueForKey:property]; + //char type = [self.class typeForProperty:property]; + + if ([value isKindOfClass:NSArray.class]) { + value = [(NSArray *) value transform:^id(id obj) { + return ([obj isKindOfClass:NSString.class] + || [obj isKindOfClass:NSNumber.class] + || [obj isKindOfClass:NSDictionary.class] + || [obj isKindOfClass:NSDate.class] + || [obj isKindOfClass:NSNull.class]) + ? obj + : [obj asDictionary]; + }]; + + } else if (!([value isKindOfClass:NSString.class] + || [value isKindOfClass:NSNumber.class] + || [value isKindOfClass:NSDictionary.class] + || [value isKindOfClass:NSDate.class] + || [value isKindOfClass:NSNull.class])) { + + value = [value asDictionary]; + } + return value; + }]; +} + +- (id)trySelector:(SEL)selector +{ + if ([self respondsToSelector:selector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + return [self performSelector:selector]; +#pragma clang diagnostic pop + } + return nil; +} + +- (id)trySelector:(SEL)selector withObject:(id)obj +{ + if ([self respondsToSelector:selector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + return [self performSelector:selector withObject:obj]; +#pragma clang diagnostic pop + } + return nil; +} + +- (id)trySelector:(SEL)selector withObject:(id)obj0 withObject:(id)obj1 +{ + if ([self respondsToSelector:selector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + return [self performSelector:selector withObject:obj0 withObject:obj1]; +#pragma clang diagnostic pop + } + return nil; +} + ++ (void)defineMethod:(SEL)selector do:(id(^)(id _self, ...))implementation +{ + class_addMethod(self, selector, imp_implementationWithBlock(implementation), "@@:@"); +} + ++ (void)defineClassMethod:(SEL)selector do:(id(^)(id _self, ...))implementation +{ + class_addMethod(object_getClass(self), selector, imp_implementationWithBlock(implementation), "@@:@"); +} + ++ (Class)subclass:(NSString *)className +{ + return objc_allocateClassPair(object_getClass(self), [className cStringUsingEncoding:NSUTF8StringEncoding], 0); +} + ++ (NSArray *)instanceMethods +{ + unsigned int count; + Method *methods = class_copyMethodList(self, &count); + NSString *methodsNames[count]; + + for (int i = 0; i < count; ++i) { + methodsNames[i] = NSStringFromSelector(method_getName(methods[i])); + } + + return [NSArray arrayWithObjects:methodsNames count:count]; +} + ++ (NSArray *)classMethods +{ + return [object_getClass(self) instanceMethods]; +} + ++ (NSArray *)instanceProperties +{ + unsigned int count; + objc_property_t *properties = class_copyPropertyList(self, &count); + NSString *propertiesNames[count]; + + for (int i = 0; i < count; ++i) { + propertiesNames[i] = [NSString stringWithUTF8String:property_getName(properties[i])]; + } + + return [NSArray arrayWithObjects:propertiesNames count:count]; +} + ++ (Class)classForProperty:(NSString *)propertyName +{ + objc_property_t property = class_getProperty(self, [propertyName cStringUsingEncoding:NSUTF8StringEncoding]); + if (!property) + return nil; // No property + const char *attr = property_getAttributes(property); + if (!attr) + return nil; // No class + + NSString *attributes = [NSString stringWithUTF8String:attr]; + + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"T@\\\"[^\\\"]+\\\"" + options:0 + error:&error]; + NSRange range = [regex rangeOfFirstMatchInString:attributes options:0 range:NSMakeRange(0, attributes.length)]; + + if (range.location == NSNotFound || range.length < 4) + return nil; // No class + + NSString *type = [attributes substringWithRange:NSMakeRange(range.location + 3, range.length - 4)]; + + return NSClassFromString(type); +} + ++ (char)typeForProperty:(NSString *)propertyName +{ + objc_property_t property = class_getProperty(self, [propertyName cStringUsingEncoding:NSUTF8StringEncoding]); + const char *attr = property_getAttributes(property); + if (!attr) + return 0; + + NSString *attributes = [NSString stringWithUTF8String:attr]; + + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"T." + options:0 + error:&error]; + NSRange range = [regex rangeOfFirstMatchInString:attributes options:0 range:NSMakeRange(0, attributes.length)]; + + if (range.location == NSNotFound || range.length < 2) + return 0; + + return [attributes characterAtIndex:range.location + 1]; +} + ++ (BOOL)hasProperty:(NSString *)propertyName +{ + return (BOOL) class_getProperty(self, [propertyName cStringUsingEncoding:NSUTF8StringEncoding]); +} + +@end