libObjCAttr/Attribute/NSObject+RFAttributes.m (123 lines of code) (raw):

// // NSObject+RFAttributes.m // libObjCAttr // // Copyright (c) 2014 EPAM Systems, Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // Redistributions in binary form must reproduce the above copyright notice, this // list of conditions and the following disclaimer in the documentation and/or // other materials provided with the distribution. // Neither the name of the EPAM Systems, Inc. nor the names of its contributors // may be used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // See the NOTICE file and the LICENSE file distributed with this work // for additional information regarding copyright ownership and licensing #import "NSObject+RFAttributesInternal.h" #import "NSRegularExpression+RFExtension.h" #import "NSObject+RFPropertyReflection.h" #import "NSObject+RFMethodReflection.h" #import "NSObject+RFMemberVariableReflection.h" @interface NSObject(RFAttributesPrivate) + (NSArray *)RF_attributesFromCreatorInvocation:(NSInvocation *)attributeCreatorValueInvocation; + (id)RF_attributeWithType:(Class)requiredClassOfAttribute from:(NSArray *)attributes; + (NSInvocation *)RF_attributeCreatorInvocationForElement:(NSString *)elementName cachedCreatorsDictionary:(NSMutableDictionary *)cachedCreatorsDictionary creatorSelectorNameFormatter:(NSString *(^)(NSString *))creatorSelectorNameFormatter; @end @implementation NSObject (RFAttributes) #pragma mark - Attributes Private API + (dispatch_queue_t)RF_sharedQueue { static dispatch_once_t onceToken; static dispatch_queue_t sharedQueue = nil; dispatch_once(&onceToken, ^{ sharedQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); }); return sharedQueue; } + (NSArray *)RF_attributesFromCreatorInvocation:(NSInvocation *)attributeCreatorValueInvocation { [attributeCreatorValueInvocation invoke]; __unsafe_unretained NSArray *result = nil; [attributeCreatorValueInvocation getReturnValue:&result]; return result; } + (id)RF_attributeWithType:(Class)requiredClassOfAttribute from:(NSArray *)attributes { id result = nil; for (NSObject *attribute in attributes) { if ([attribute isKindOfClass:requiredClassOfAttribute]) { result = attribute; break; } } return result; } + (NSInvocation *)RF_attributeCreatorInvocationForElement:(NSString *)elementName cachedCreatorsDictionary:(NSMutableDictionary *)cachedCreatorsDictionary creatorSelectorNameFormatter:(NSString *(^)(NSString *))creatorSelectorNameFormatter { NSInvocation *result = cachedCreatorsDictionary[elementName]; if (result) { return result; } NSString *creatorSelectorName = creatorSelectorNameFormatter(elementName); SEL creatorSelector = NSSelectorFromString(creatorSelectorName); if (!creatorSelector) { return nil; } result = [self RF_invocationForSelector:creatorSelector]; if (!result) { return nil; } dispatch_sync([self RF_sharedQueue], ^{ cachedCreatorsDictionary[elementName] = result; }); return result; } #pragma mark - Attributes Public API + (NSArray *)RF_attributesForMethod:(NSString *)methodName { NSInvocation *attributeCreatorInvocation = [self RF_attributeCreatorInvocationForElement:methodName cachedCreatorsDictionary:self.RF_attributesFactoriesForMethods creatorSelectorNameFormatter:^NSString *(NSString *methodNameToFormat) { NSUInteger parametersCount = [NSRegularExpression RF_numberOfMatchesToRegex:@":" inString:methodNameToFormat]; NSString *methodNameWithoutParameters = [NSRegularExpression RF_stringByReplacingRegex:@":.*" withTemplate:@"" inString:methodNameToFormat]; return [NSString stringWithFormat:@"RF_attributes_%@_method_%@_p%tu", NSStringFromClass(self), methodNameWithoutParameters, parametersCount]; }]; return [self RF_attributesFromCreatorInvocation:attributeCreatorInvocation]; } + (NSArray *)RF_attributesForProperty:(NSString *)propertyName { NSInvocation *attributeCreatorInvocation = [self RF_attributeCreatorInvocationForElement:propertyName cachedCreatorsDictionary:self.RF_attributesFactoriesForProperties creatorSelectorNameFormatter:^NSString *(NSString *propertyNameToFormat) { return [NSString stringWithFormat:@"RF_attributes_%@_property_%@", NSStringFromClass(self), propertyNameToFormat]; }]; return [self RF_attributesFromCreatorInvocation:attributeCreatorInvocation]; } + (NSArray *)RF_attributesForIvar:(NSString *)ivarName { NSInvocation *attributeCreatorInvocation = [self RF_attributeCreatorInvocationForElement:ivarName cachedCreatorsDictionary:self.RF_attributesFactoriesForIvars creatorSelectorNameFormatter:^NSString *(NSString *ivarNameToFormat) { return [NSString stringWithFormat:@"RF_attributes_%@_ivar_%@", NSStringFromClass(self), ivarNameToFormat]; }]; return [self RF_attributesFromCreatorInvocation:attributeCreatorInvocation]; } + (NSArray *)RF_attributesForClass { return nil; } + (id)RF_attributeForMethod:(NSString *)methodName withAttributeType:(Class)requiredClassOfAttribute { NSAssert(requiredClassOfAttribute, @"You must specify class of required attribute"); return [self RF_attributeWithType:requiredClassOfAttribute from:[self RF_attributesForMethod:methodName]]; } + (id)RF_attributeForProperty:(NSString *)propertyName withAttributeType:(Class)requiredClassOfAttribute { NSAssert(requiredClassOfAttribute, @"You must specify class of required attribute"); return [self RF_attributeWithType:requiredClassOfAttribute from:[self RF_attributesForProperty:propertyName]]; } + (id)RF_attributeForIvar:(NSString *)ivarName withAttributeType:(Class)requiredClassOfAttribute { NSAssert(requiredClassOfAttribute, @"You must specify class of required attribute"); return [self RF_attributeWithType:requiredClassOfAttribute from:[self RF_attributesForIvar:ivarName]]; } + (id)RF_attributeForClassWithAttributeType:(Class)requiredClassOfAttribute { NSAssert(requiredClassOfAttribute, @"You must specify class of required attribute"); return [self RF_attributeWithType:requiredClassOfAttribute from:[self RF_attributesForClass]]; } + (NSArray *)RF_propertiesWithAttributeType:(Class)requiredClassOfAttribute { NSMutableArray *result = [NSMutableArray array]; for (RFPropertyInfo *currentPropertyInfo in [self RF_properties]) { if ([currentPropertyInfo attributeWithType:requiredClassOfAttribute]) { [result addObject:currentPropertyInfo]; } } return result; } + (NSArray *)RF_ivarsWithAttributeType:(Class)requiredClassOfAttribute { NSMutableArray *result = [NSMutableArray array]; for (RFIvarInfo *currentIvarInfo in [self RF_ivars]) { if ([currentIvarInfo attributeWithType:requiredClassOfAttribute]) { [result addObject:currentIvarInfo]; } } return result; } + (NSArray *)RF_methodsWithAttributeType:(Class)requiredClassOfAttribute { NSMutableArray *result = [NSMutableArray array]; for (RFMethodInfo *currentMethodInfo in [self RF_methods]) { if ([currentMethodInfo attributeWithType:requiredClassOfAttribute]) { [result addObject:currentMethodInfo]; } } return result; } @end