2012-02-29 2 views
17

NSObject에 카테고리가 있으므로 물건이 필요합니다. 내가 객체를 호출 할 때 dealloc 메소드를 오버라이드하여 정리를하고 싶습니다.클래스가 아닌 단일 인스턴스 Swizzling

나는 방법을 사용하여 그것을하고 싶었지만 방법을 알 수 없었습니다. 내가 찾은 유일한 예는 전체 클래스의 메소드 구현을 대체하는 방법에 관한 것입니다. 제 경우에는 모든 NSObject에 대한 dealloc을 오버라이드합니다.

NSObject의 특정 인스턴스에 대한 dealloc 메소드를 무시하고 싶습니다.

@interface NSObject(MyCategory) 
-(void)test; 
@end 

@implementation NSObject(MyCategory) 
-(void)newDealloc 
{ 
    // do some cleanup here 
    [self dealloc]; // call actual dealloc method 
} 
-(void)test 
{ 
    IMP orig=[self methodForSelector:@selector(dealloc)]; 
    IMP repl=[self methodForSelector:@selector(newDealloc)]; 
    if (...) // 'test' might be called several times, this replacement should happen only on the first call 
    { 
    method_exchangeImplementations(..., ...); 
    } 
} 
@end 

답변

18

개체에는 자체 메서드 테이블이 없으므로 실제로는이 작업을 수행 할 수 없습니다. 클래스 만 메소드 테이블을 가지며,이를 변경하면 해당 클래스의 모든 오브젝트에 영향을줍니다. 이것에 대한 간단한 방법이 있습니다 : 런타임에 객체의 클래스를 동적으로 생성 된 서브 클래스로 변경합니다. isa-swizzling이라고도하는이 기법은 Apple에서 자동 KVO를 구현하는 데 사용됩니다.

이것은 강력한 방법이며 용도가 있습니다. 그러나 귀하의 경우 관련 객체를 사용하는보다 쉬운 방법이 있습니다. 기본적으로 objc_setAssociatedObject을 사용하여 dealloc에서 정리를 수행하는 첫 번째 개체에 다른 개체를 연결합니다. this blog post on Cocoa is my Girlfriend에서 자세한 내용을 확인할 수 있습니다.

+0

답변 해 주셔서 감사합니다. 그래서 기본적으로, 각 호출마다 - 나는 (구현하는) 새로운 객체를 할당하고 그것을 연관된 객체로 설정하고, 그 dealloc 내부에서 정리를 수행합니까? –

+0

바로. 그 블로그 포스트를 보면 NSObject에 멋진 카테고리가있어서 어떤 객체의 dealloc 중에 호출 될 블록을 등록 할 수 있습니다. – Sven

8

메서드 선택은 개체 인스턴스의 클래스을 기반으로하므로 메서드 스윕은 사용자가 발견 한 동일한 클래스의 모든 인스턴스에 영향을줍니다.

하지만 인스턴스의 클래스는 변경할 수 있지만주의해야합니다! 여기에 윤곽이, 당신은 클래스가 있다고 가정 :

이제
@instance MyPlainObject : NSObject 

- (void) doSomething; 

@end 

단지 MyPlainObject의 인스턴스 중 일부는 당신이 첫 번째 서브 클래스 정의 doSomething의 동작을 변경하고 싶습니다위한 경우 :

@instance MyFancyObject: MyPlainObject 

- (void) doSomething; 

@end 

이제는 MyFancyObject 인스턴스를 분명하게 만들 수 있지만, 우리가해야 할 일은 기존의 인스턴스를 MyPlainObject으로 가져와 MyFancyObject으로 만들어 새로운 동작을 얻으려고합니다. 우리는 클래스를 스위 즐링 (swizzle) 수를 들어, 다음을 추가 MyFancyObject에 :

MyPlainClass *mpc = [MyPlainClass new]; 

... 

// masquerade as MyFancyClass 
[MyFancyClass changeKind:mpc fancy:YES] 

... // mpc behaves as a MyFancyClass 

// revert to true nature 
[MyFancyClass changeKind:mpc: fancy:NO]; 
: 어떤 원래 당신이 MyFancyClass로 동작하도록 전환 할 수 있습니다 MyPlainClass의 경우, 그 반대로 지금

static Class myPlainObjectClass; 
static Class myFancyObjectClass; 

+ (void)initialize 
{ 
    myPlainObjectClass = objc_getClass("MyPlainObject"); 
    myFancyObjectClass = objc_getClass("MyFancyObject"); 
} 

+ (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy 
{ 
    object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass); 
} 

주의 사항이의

(일부) :

당신은 이 서브 클래스의 재정의 경우이 작업을 수행 또는 방법을 추가하고,를 추가 할 수 있습니다(클래스) 변수.

또한 동작을 변경하려는 영속 클래스의 하위 클래스가 필요하며 여러 클래스의 동작을 변경할 수있는 단일 클래스를 가질 수 없습니다.

+0

하지만 그 경우, 나는 그 인스턴스의 클래스가 무엇인지 미리 알아야합니다. 필자의 경우 객체는 아무 것도 될 수 있습니다 (SDK 자체에서 생성 된 객체조차도). 따라서 유형을 알 수 있다고 가정 할 수 없습니다. –

+0

네, 그게 하나의 경고입니다! 필요한 경우 연관된 객체 경로로 이동하십시오. 그러나 특정 클래스를 타겟팅하는 경우 swizzling은 관련 객체보다 조금 간단하고 (주관적인) 물론 모든 메소드를 쉽게 재정의 할 수 있습니다. – CRD

+0

관련 객체를 솔루션으로 사용했지만 답변에 감사드립니다. 나는 그것에 대해 질문이있다 : 메소드를 오버라이드/추가하는 것만 유용하기 때문에 - 이것과 카테고리간에 다른 점은 무엇인가? –

1

나는 스윙을하는 API를 만들었습니다.이 API는 또한 인스턴스 스윙을 특징으로합니다.나는 이것이 정확히 당신이 찾고있는 것 같아요 : https://github.com/JonasGessner/JGMethodSwizzler

그것은 런타임에 swizzling 특정 인스턴스에 대한 동적 하위 클래스를 생성하여 작동합니다.