2012-07-05 4 views
4

클래스의 "인스턴스"에 인스턴스 메소드를 추가하는 방법이 있는지 궁금합니다.런타임에 objective-c 클래스의 인스턴스에 메소드 추가하기

시나리오는 내가 EKEventEditViewController를 사용하고 있으며이 클래스 내부에 "EKEventEditor"(비공개 AFAIK)라는 위임 (UITableViewDelegate)이있는 UITableView가 있습니다. 그것은 일부 셀을 비활성화하는 데 사용하려고하는 tableView : willDisplayCell : forRowAtIndexPath : 메서드를 구현하지 않습니다.

그래서 인스턴스에 메서드를 추가하려고하지만 Obj-C 런타임에서 찾을 수있는 것은 클래스에 메서드를 추가하지만 인스턴스는 추가하지 않는 class_addMethod 호출입니다. "EKEventEditor"가 비공개인데, 나는 그것을 확장하고 그 방법을 직접 추가 할 수 없습니다.

힌트가 있습니까?

여기에 내가 사용하려고하는 코드 (willDisplayCell_)를 추가하려고하는 함수가 호출되지 않습니다.

- (void)navigationController:(UINavigationController *)navigationController 
    willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { 

if ([navigationController isKindOfClass:[EKEventEditViewController class]]) { 

UITableViewController *rootController = (UITableViewController*)[(UINavigationController*)navigationController visibleViewController]; 
if ([rootController isKindOfClass:[UITableViewController class]]) { 
    UITableView *eventTableView = (UITableView*)[rootController view]; 

    class_addMethod([eventTableView.delegate class], 
        @selector(tableView:willDisplayCell:forRowAtIndexPath:), 
        (IMP)willDisplayCell_, "[email protected]:@@@"); 
}}} 


void willDisplayCell_(id self, SEL cmd, UITableView *tableView, UITableViewCell *cell, NSIndexPath *indexPath) { 
    NSLog(@"CALLED?"); 
} 

답변

2

This is totally possible, and amazingly useful는 ... 등, 서브 클래스없이 인스턴스의 기능을 변경하는 생각 ...

@interface NSObject (AZOverride) 
/** Dynamically overrides the specified method on this particular instance. 
* The block's parameters and return type must match those of the method you 
* are overriding. However, the first parameter is always "id _self", which 
* points to the object itself. 
* You do have to cast the block's type to (__bridge void *), e.g.: 
* 
* [self az_overrideSelector:@selector(viewDidAppear:) 
*   withBlock:(__bridge void *)^(id _self, BOOL animated) { ... }]; 
*/ 
- (BOOL)az_overrideSelector:(SEL)selector withBlock:(void *)block; 
/** To call super from the overridden method, do the following: 
* SEL sel = @selector(viewDidAppear:); 
* void (*superIMP)(id, SEL, BOOL) = [_self az_superForSelector:sel]; 
* superIMP(_self, sel, animated); 
* This first gets a function pointer to the super method and then you call it. 
*/ 
- (void *)az_superForSelector:(SEL)selector; 
@end 
3

원하는 기능입니다. 즉 모든 인스턴스 클래스의에 (예) 메소드를 추가합니다. * 방법 단지 하나 개의 특정 인스턴스를 추가 할 수있는 방법이 없습니다. **

당신은 그러나, Category을 사용하여 조사 할 수 있습니다

, 어떤 을 사용하여 사용자가 제어하지 않는 @implementation의 클래스를 확장 할 수 있습니다. 클래스의 기본 @interface을 가져올 수있는 경우에만 작동하며, 그렇지 않을 수도 있습니다.


* 클래스 메서드를 추가하려면 메타 클래스를 첫 번째 인수로 사용하여 함수를 호출합니다.

** 내가 작업하고있는 forwardInvocation: 및 가능한 libffi를 재정의하여 일부 속임수를 배제하십시오.

+1

당신은 할 수 KVO가 개별 객체의 접근자를 동적으로 덮어 쓰는 데 사용하는 것과 동일한 하위 클래스 및 속임수를 사용하여 특정 인스턴스에 메소드를 추가합니다. – Chuck

+0

@ Chuck : 오, 그래! 날 따라와. –

+0

@JoshCaswell 나는 내 코드로 질문을 편집했다. 나는 willDisplayCell_이 호출되지 않는다고 잘못된 것을하고 있는가? – Herman

2

여기에 REKit입니다. 인스턴스 수준에서 메소드를 추가/대체하는 기능을 제공합니다. REKit와

, 당신은 아래와 같이 eventTableView.delegate에 방법을 추가 할 수 있습니다

[eventTableView.delegate 
    respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:) 
    withKey:nil 
    usingBlock:^(id receiver, UITableView *tableView, UITableViewCell *cell, NSIndexPath *indexPath) { 
     // Do something… 
    } 
]; 
+0

이 또한 작동하지 않습니다. –

관련 문제