Skip to content

Instantly share code, notes, and snippets.

@jidolstar
Created January 15, 2019 13:14
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 jidolstar/606b6a6d5aa7f7377d94d22d163d91f3 to your computer and use it in GitHub Desktop.
Save jidolstar/606b6a6d5aa7f7377d94d22d163d91f3 to your computer and use it in GitHub Desktop.
bsReflection : objective-c
#import <Foundation/Foundation.h>
//Reflection
@interface bsReflection : NSObject
+(NSString*)stringFromClass:(Class)clazz; //클래스로부터 클래스 이름을 가져온다.
+(Class)classFromString:(NSString*)className; //클래스 이름으로부터 클래스를 가져온다.
+(Class)classFromObject:(id)object; //객체로부터 클래스를 가져온다.
+(NSString*)classNameFromObject:(id)object; //객체로부터 클래스 이름을 가져온다.
+(Class)getPropClassOfObject:(id)object key:(NSString*)key; //객체의 프로퍼티의 클래스를 가져온다.
+(Class)getPropClassOfClass:(Class)clazz key:(NSString*)key; //클래스의 프로퍼티의 클래스를 가져온다.
+(Class)getPropClassOfRootObject:(id)object keyPath:(NSString*)keyPath; //기반객체로부터 keyPath에 해당하는 속성의 클래스를 얻어온다.
+(Class)getPropClassOfRootClass:(Class)clazz keyPath:(NSString*)keyPath; //기반클래스로부터 keyPath에 해당하는 속성의 클래스를 얻어온다.
+(NSArray*)getPropNamesOfClass:(Class)clazz superInquiry:(BOOL)superInquiry; //클래스의 프로퍼티 이름을 배열로 가져온다. superInquiry는 해당클래스의 부모클래스 프로퍼티도 탐색할 것인지 결정하는 플래그다.
+(id)getPropValueOfObject:(id)object keyPath:(NSString*)keyPath; //객체에서 주어진 이름을 가진 프로퍼티의 값을 가져온다.
+(void)setPropValueOfObject:(id)object keyPath:(NSString*)keyPath value:(id)value; //객체에서 주어진 이름을 가진 프로퍼티의 값을 셋팅한다.(KVO, KVC에 적용안됨)
+(BOOL)hasPropAtObject:(id)object keyPath:(NSString*)keyPath; //객체에서 주어진 이름의 프로퍼티를 가지고 있는가?
+(BOOL)hasPropAtClass:(Class)clazz keyPath:(NSString*)keyPath; //클래스에서 주어진 이름의 프로퍼티를 가지고 있는가?
@end
#import "bsReflection.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "bs.h"
@implementation bsReflection
//클래스로부터 클래스 이름을 가져온다.
+(NSString*)stringFromClass:(Class)clazz {
return NSStringFromClass( clazz );
}
//클래스 이름으로부터 클래스를 가져온다.
+(Class)classFromString:(NSString*)className {
return NSClassFromString( className );
}
//객체로부터 클래스를 가져온다.
+(Class)classFromObject:(id)object {
return [object class];
}
//객체로부터 클래스 이름을 가져온다.
+(NSString*)classNameFromObject:(id)object {
return NSStringFromClass( [object class] );
}
//객체의 프로퍼티의 클래스를 가져온다.
+(Class)getPropClassOfObject:(id)object key:(NSString*)key {
return [self getPropClassOfClass:[object class] key:key];
}
//클래스의 프로퍼티의 클래스를 가져온다.
+(Class)getPropClassOfClass:(Class)clazz key:(NSString*)key {
const char *nm = [key UTF8String];
objc_property_t p0 = class_getProperty( clazz, nm );
if( p0 == NULL ) {
return NULL;
}
NSString *attr = [NSString stringWithFormat:@"%s", property_getAttributes( p0 )];
NSArray *attrSplit = [attr componentsSeparatedByString:@"\""]; //"T@"NSString",R,V_test"에서 NSString만 추출해야 한다.
NSString *className = nil;
if ([attrSplit count] >= 2) {
className = [attrSplit objectAtIndex:1];
}
if( className == nil ) return NULL;
return NSClassFromString( className );
}
//기반객체로부터 keyPath에 해당하는 속성의 클래스를 얻어온다.
+(Class)getPropClassOfRootObject:(id)object keyPath:(NSString*)keyPath {
return [self getPropClassOfRootClass:[object class] keyPath:keyPath];
}
//기반클래스로부터 keyPath에 해당하는 속성의 클래스를 얻어온다.
+(Class)getPropClassOfRootClass:(Class)clazz keyPath:(NSString*)keyPath {
NSArray *names = [bs strSplit:keyPath seperator:@"." trim:NO];
if( [names count] == 0 ) return NULL;
__block Class c = clazz;
[names enumerateObjectsUsingBlock:^(NSString *nm, NSUInteger idx, BOOL *stop) {
objc_property_t p = class_getProperty(c, [nm UTF8String]);
if( !p ) {
*stop = YES;
return;
}
NSString *attr = [NSString stringWithFormat:@"%s", property_getAttributes( p )];
NSArray *attrSplit = [attr componentsSeparatedByString:@"\""];
if ( [attrSplit count] < 2) {
*stop = YES;
c = NULL;
}
NSString *className = [attrSplit objectAtIndex:1];
c = NSClassFromString( className );
}];
return c;
}
//클래스의 프로퍼티 이름을 배열로 가져온다. superInquiry는 해당클래스의 부모클래스 프로퍼티도 탐색할 것인지 결정하는 플래그다.
+(NSArray*)getPropNamesOfClass:(Class)clazz superInquiry:(BOOL)superInquiry{
if( clazz == NULL || clazz == [NSObject class] ) {
return nil;
}
NSMutableArray *r = [[NSMutableArray alloc] init];
unsigned int count, i;
objc_property_t *ps = class_copyPropertyList( clazz, &count );
for( i = 0; i < count; i++ ) {
objc_property_t p = ps[i];
const char *pn = property_getName( p );
if( pn ) {
[r addObject:[NSString stringWithUTF8String:pn]];
}
}
free( ps );
if( superInquiry ) {
NSArray *sr = [self getPropNamesOfClass:[clazz superclass] superInquiry:YES];
if( sr != nil ) [r addObjectsFromArray:sr];
}
return [NSArray arrayWithArray:r];
}
//객체에서 주어진 이름을 가진 프로퍼티의 값을 가져온다.
+(id)getPropValueOfObject:(id)object keyPath:(NSString*)keyPath {
NSArray *t0 = [bs strSplit:keyPath seperator:@"." trim:NO];
if( [t0 count] > 0 ) {
__block id t00 = object;
[t0 enumerateObjectsUsingBlock:^(NSString *name, NSUInteger idx, BOOL *stop) {
Ivar ivar = class_getInstanceVariable([t00 class], [[NSString stringWithFormat:@"_%@", name] UTF8String]);
t00 = object_getIvar( t00, ivar );
if( t00 == nil ) *stop = YES;
}];
return t00;
}
return nil;
}
//객체에서 주어진 이름을 가진 프로퍼티의 값을 셋팅한다.(KVO, KVC에 적용안됨)
+(void)setPropValueOfObject:(id)object keyPath:(NSString*)keyPath value:(id)value {
NSArray *t0 = [bs strSplit:keyPath seperator:@"." trim:NO];
if( [t0 count] > 0 ) {
id t00 = object;
Ivar ivar = nil;
long i = 0, j = [t0 count];
while( 1 ) {
ivar = class_getInstanceVariable([t00 class], [[NSString stringWithFormat:@"_%@", t0[i]] UTF8String]);
if( ++i < j ) {
t00 = object_getIvar( t00, ivar );
if( t00 == nil ) break;
} else {
break;
}
}
object_setIvar( t00, ivar, value );
}
}
//객체에서 주어진 이름의 프로퍼티를 가지고 있는가?
+(BOOL)hasPropAtObject:(id)object keyPath:(NSString*)keyPath {
return [self hasPropAtClass:[object class] keyPath:keyPath];
}
//클래스에서 주어진 이름의 프로퍼티를 가지고 있는가?
+(BOOL)hasPropAtClass:(Class)clazz keyPath:(NSString*)keyPath {
//clazz와 name으로 캐싱필요!
NSArray *names = [bs strSplit:keyPath seperator:@"." trim:NO];
if( [names count] == 0 ) return NO;
__block Class c = clazz;
__block BOOL has = NO;
NSUInteger maxIdx = [names count] - 1;
[names enumerateObjectsUsingBlock:^(NSString *nm, NSUInteger idx, BOOL *stop) {
objc_property_t p = class_getProperty(c, [nm UTF8String]);
if ( !p ) {
has = NO;
*stop = YES;
return;
}
if( idx == maxIdx ) {
has = YES;
return;
}
NSString *attr = [NSString stringWithFormat:@"%s", property_getAttributes( p )];
NSArray *attrSplit = [attr componentsSeparatedByString:@"\""];
if ( [attrSplit count] < 2) {
has = NO;
*stop = YES;
return;
}
NSString *className = [attrSplit objectAtIndex:1];
c = NSClassFromString( className );
}];
return has;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment