4

我想知道是否有办法将实例方法添加到类的“实例”中。

场景是我正在使用 EKEventEditViewController,并且在这个类中有一个 UITableView,有一个名为“EKEventEditor”(非公共 AFAIK)的委托(UITableViewDelegate)。它没有实现 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_, "v@:@@@");
}}} 


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

3 回答 3

3

这就是你想要的功能。这会将(实例)方法添加到类的所有实例中。* 无法仅将方法添加到一个特定实例中。**

但是,您可能想考虑使用Category,它确实允许您扩展@implementation您无法控制的类。但是,这仅在您可以导入该类的 primary 时才有效@interface,而且听起来可能并非如此。


*如果你想添加一个类方法,你可以用元类作为第一个参数来调用函数。

forwardInvocation:** 除非我正在处理一些覆盖和可能的 libffi 的诡计。

于 2012-07-05T23:52:41.860 回答
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…
    }
];
于 2013-02-14T05:41:43.330 回答
2

这是完全可能的,而且非常有用......考虑在没有子类化的情况下改变实例的功能等等......

@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
于 2013-06-25T07:52:37.990 回答