libObjCAttr/Reflection/RFPropertyInfo.m (201 lines of code) (raw):
//
// RFPropertyInfo.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 "RFPropertyInfo.h"
#import <objc/runtime.h>
#import "ROADAttribute.h"
#import "RFTypeDecoder.h"
@interface RFPropertyInfo () {
NSString * _propertyName;
NSString * _className;
Class _hostClass;
NSString * _typeName;
NSString * _setterName;
NSString * _getterName;
BOOL _dynamic;
BOOL _weak;
BOOL _nonatomic;
BOOL _strong;
BOOL _readonly;
BOOL _copied;
BOOL _primitive;
Class _typeClass;
objc_property_t _property;
BOOL _isSpecifiersFilled;
BOOL _isAttributeNameFilled;
}
@property (copy, nonatomic) NSString *propertyName;
@property (assign, nonatomic) Class hostClass;
@end
@implementation RFPropertyInfo
@dynamic attributes;
#pragma mark - Initialization
+ (NSArray *)propertiesForClass:(Class)aClass {
return [self propertiesForClass:aClass depth:1];
}
+ (NSArray *)propertiesForClass:(Class)aClass depth:(NSUInteger)depth {
if (depth <= 0) {
return @[];
}
NSMutableArray *result = [[NSMutableArray alloc] init];
unsigned int numberOfProperties = 0;
objc_property_t *propertiesArray = class_copyPropertyList(aClass, &numberOfProperties);
for (unsigned int idx = 0; idx < numberOfProperties; idx++) {
[result addObject:[self property:propertiesArray[idx] forClass:aClass]];
}
free(propertiesArray);
[result addObjectsFromArray:[self propertiesForClass:class_getSuperclass(aClass) depth:--depth]];
return result;
}
+ (RFPropertyInfo *)RF_propertyNamed:(NSString *)name forClass:(Class)aClass {
objc_property_t prop = class_getProperty(aClass, [name cStringUsingEncoding:NSUTF8StringEncoding]);
RFPropertyInfo *result = nil;
if (prop != NULL) {
result = [self property:prop forClass:aClass];
}
return result;
}
+ (NSArray *)propertiesForClass:(Class)class withPredicate:(NSPredicate *)aPredicate {
NSArray *result = [self propertiesForClass:class];
return [result filteredArrayUsingPredicate:aPredicate];
}
// For reference see apple's documetation about declared properties:
// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
+ (RFPropertyInfo *)property:(objc_property_t)property forClass:(Class)class {
RFPropertyInfo * const info = [[RFPropertyInfo alloc] initWithProperty:property];
info.hostClass = class;
return info;
}
+ (NSString *)propertyAttributeNameForField:(const char *)fieldName property:(const objc_property_t)property {
NSString *result;
char *name = property_copyAttributeValue(property, fieldName);
if (name != NULL) {
result = @(name);
free(name);
}
return result;
}
+ (BOOL)property:(const objc_property_t)property containsSpecifier:(const char *)specifier {
char *attributeValue = property_copyAttributeValue(property, specifier);
BOOL const result = attributeValue != NULL;
free(attributeValue);
return result;
}
- (id)initWithProperty:(objc_property_t)property {
self = [super init];
if (self) {
_property = property;
_isSpecifiersFilled = NO;
_isAttributeNameFilled = NO;
}
return self;
}
- (NSArray *)attributes {
return [self.hostClass RF_attributesForProperty:self.propertyName];
}
- (id)attributeWithType:(Class)requiredClassOfAttribute {
return [self.hostClass RF_attributeForProperty:self.propertyName withAttributeType:requiredClassOfAttribute];
}
#pragma mark - Specifiers
- (NSString *)propertyName {
if (!_propertyName) {
_propertyName = @(property_getName(_property));
}
return _propertyName;
}
- (NSString *)className {
if (!_className) {
_className = NSStringFromClass(self.hostClass);
}
return _className;
}
- (NSString *)typeName {
if (!_isAttributeNameFilled) {
[self fillAttributeName];
}
return _typeName;
}
- (Class)typeClass {
if (!_typeClass) {
if (!_isAttributeNameFilled) {
[self fillAttributeName];
}
_typeClass = NSClassFromString([RFTypeDecoder RF_classNameFromTypeName:_typeName]);
}
return _typeClass;
}
- (BOOL)isPrimitive {
if (!_isAttributeNameFilled) {
[self fillAttributeName];
}
return _primitive;
}
- (BOOL)isDynamic {
if (!_isSpecifiersFilled) {
[self fillSpecifiers];
}
return _dynamic;
}
- (BOOL)isWeak {
if (!_isSpecifiersFilled) {
[self fillSpecifiers];
}
return _weak;
}
- (BOOL)isNonatomic {
if (!_isSpecifiersFilled) {
[self fillSpecifiers];
}
return _nonatomic;
}
- (BOOL)isReadonly {
if (!_isSpecifiersFilled) {
[self fillSpecifiers];
}
return _readonly;
}
- (BOOL)isStrong {
if (!_isSpecifiersFilled) {
[self fillSpecifiers];
}
return _strong;
}
- (BOOL)isCopied {
if (!_isSpecifiersFilled) {
[self fillSpecifiers];
}
return _copied;
}
- (NSString *)getterName {
if (!_getterName) {
_getterName = [[self class] propertyAttributeNameForField:"G" property:_property];
}
return _getterName;
}
- (NSString *)setterName {
if (!_setterName) {
_setterName = [[self class] propertyAttributeNameForField:"S" property:_property];
}
return _setterName;
}
#pragma mark - Utility methods
static const char * kPropertyInfoDynamicSpecifier = "D";
static const char * kPropertyInfoWeakSpecifier = "W";
static const char * kPropertyInfoNonatomicSpecifier = "N";
static const char * kPropertyInfoReadonlySpecifier = "R";
static const char * kPropertyInfoStrongSpecifier = "&";
static const char * kPropertyInfoCopiedSpecifier = "C";
- (void)fillSpecifiers {
_dynamic = [[self class] property:_property containsSpecifier:kPropertyInfoDynamicSpecifier];
_weak = [[self class] property:_property containsSpecifier:kPropertyInfoWeakSpecifier];
_nonatomic = [[self class] property:_property containsSpecifier:kPropertyInfoNonatomicSpecifier];
_readonly = [[self class] property:_property containsSpecifier:kPropertyInfoReadonlySpecifier];
_strong = [[self class] property:_property containsSpecifier:kPropertyInfoStrongSpecifier];
_copied = [[self class] property:_property containsSpecifier:kPropertyInfoCopiedSpecifier];
_isSpecifiersFilled = YES;
}
- (void)fillAttributeName {
NSString *attributeName = [[self class] propertyAttributeNameForField:"T" property:_property];
_typeName = [RFTypeDecoder nameFromTypeEncoding:attributeName];
_primitive = [RFTypeDecoder RF_isPrimitiveType:attributeName];
_isAttributeNameFilled = YES;
}
- (NSString *)description {
return [[NSString alloc] initWithFormat:@"%@: hostClass = %@, property name = %@", NSStringFromClass([self class]), NSStringFromClass([self.hostClass class]), self.propertyName];
}
@end