2012-11-30 2 views
10

런타임에 임의의 수의 동적 속성을 가질 수있는 Objective-C 클래스를 만들 수 있습니까?런타임시 Objective-C 동적 속성?

mySpecialClass.anyProperty을 호출하고 예외를 발생시키면서 런타임에 NSString (예를 들어)을 반환 할 수있는 내 자신의 사용자 지정 구현을 제공 할 수 있도록이 클래스 내부에서이를 가로 챌 수 있어야합니다. 분명히이 모든 것은 컴파일해야합니다.

예를 들어 새로운 리터럴 구문과 비슷한 것을 사용하여 내 속성을 참조 할 수 있다면 이상적입니다. mySpecialClass["anyProperty"].

동적 인 NSDictionary와 같은 것을 만들고 싶습니다. CFDictionary 백업 스토어가 없는데, 속성에 대한 2 개의 사용자 정의 메소드를 실행하고 속성 이름을 각각의 접근 자 메소드에 전달하여 각각 결정할 수 있습니다. 무엇을 해야할지.

+0

예, 가능합니다. –

+1

(세 가지 기본 메커니즘이 있습니다 : 1) 실제로 클래스에 메소드를 추가합니다. 2) "인식 할 수없는 메시지"오류를 가로 채서 구현을 제공합니다. 3) 속성에 setter 및 getter를 가로채는 특정 방법이 있습니다. 그러나 나는 세부 사항을 가지고 있지 않다.) –

답변

32

적어도 두 가지 방법이 있습니다.

사용 objectForKeyedSubscript: 및 첨자

setObject:forKeyedSubscript:

@property (nonatomic,strong) NSMutableDictionary *properties; 

- (id)objectForKeyedSubscript:(id)key { 
     return [[self properties] valueForKey:[NSString stringWithFormat:@"%@",key]]; 
} 

- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key { 
     [[self properties] setValue:object forKey:[NSString stringWithFormat:@"%@",key]]; 
} 

Person *p = [Person new]; 
p[@"name"] = @"Jon"; 
NSLog(@"%@",p[@"name"]); 

resolveInstanceMethod :

이 모든 방법에 대한 런타임에 의해 objc_sendMsg을 실행 :

objc_sendMsg

아래쪽을 보면 resolveInstanceMethod:으로 메쏘드 호출을 원하는 방향으로 리디렉션 할 수 있습니다. 클래스에 요청 메소드를 추가 resolveInstanceMethod:을 구현 한 후

// generic getter 
static id propertyIMP(id self, SEL _cmd) { 
    return [[self properties] valueForKey:NSStringFromSelector(_cmd)]; 
} 


// generic setter 
static void setPropertyIMP(id self, SEL _cmd, id aValue) { 

    id value = [aValue copy]; 
    NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy]; 

    // delete "set" and ":" and lowercase first letter 
    [key deleteCharactersInRange:NSMakeRange(0, 3)]; 
    [key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)]; 
    NSString *firstChar = [key substringToIndex:1]; 
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]]; 

    [[self properties] setValue:value forKey:key]; 
} 

그리고 : 귀하의 질문에 대답하기 위해, 당신은 보이는 최대 값을 사전에 바르에 일반적인 getter 및 setter를 작성해야합니다.

+ (BOOL)resolveInstanceMethod:(SEL)aSEL { 
    if ([NSStringFromSelector(aSEL) hasPrefix:@"set"]) { 
     class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "[email protected]:@"); 
    } else { 
     class_addMethod([self class], aSEL,(IMP)propertyIMP, "@@:"); 
    } 
    return YES; 
} 

당신은 또한 그런 다음있는 NSInvocation에 싸여 forwardInvocation:에 전달되는 방법에 대한 NSMethodSignature를 반환,하지만 방법을 추가하는 것은 빠르다 수 있습니다.

Here is a gist CodeRunner에서 실행됩니다. myClass["anyProperty"] 호출은 처리하지 않습니다.

+5

나는이 대답의 awesomeness를 지적하고 싶다. –

+1

코드는 Rob Napier와 Mugunth Kumar가 작성한 "iOS 5 프로그래밍의 한계 극복"에서 발췌 한 것입니다. 20 장 : 심화 목표 -C는 @dynamic 속성을 구현하는 방법을 설명합니다. 이 책에는 그와 같은 몇 가지 고급 기법이 있습니다. 나는 Omnigraffle과 함께 책과 여러 장소의 정보로 그래픽을 만들었습니다. – Jano

+2

+1 ... [Mike Ash가 지적한대로] (http://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html) 형식 코드를 하드 코딩하지 말고 검색하는 것이 좋습니다. – wbyoung

3

당신은 다른 것을 요구하고 있습니다. 클래스의 인스턴스에 대괄호 구문 mySpecialClass[@"anyProperty"]을 사용할 수있게하려면 매우 쉽습니다.

- (id)objectForKeyedSubscript:(id)key 
{ 
     return ###something based on the key argument### 
} 

- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key 
{ 
     ###set something with object based on key#### 
} 

소스 코드에서 대괄호 구문을 사용할 때마다 호출됩니다. 당신은 런타임에 속성을 만들려면

그렇지 않으면, ..., 진행 NSObjectforwardInvocation: 방법을 살펴, 또는 동적으로 클래스를 변경하는 기능을위한 Objective-C Runtime Reference 보는 여러 가지 방법이 있습니다