【问题标题】:How do I call +class methods in Objective C without referencing the class?如何在不引用类的情况下在 Objective C 中调用 +class 方法?
【发布时间】:2009-05-31 12:07:24
【问题描述】:

我有一系列“策略”对象,我认为这些对象可以方便地实现为一组策略类的类方法。我已经为此指定了一个协议,并创建了符合的类(如下所示)

@protocol Counter   
+(NSInteger) countFor: (Model *)model;
@end

@interface CurrentListCounter : NSObject <Counter> 
+(NSInteger) countFor: (Model *)model;
@end

然后我有一个符合这个协议的类的数组(就像 CurrentListCounter 一样)

+(NSArray *) availableCounters {
return [[[NSArray alloc] initWithObjects: [CurrentListCounter class], [AllListsCounter class], nil] autorelease];
}

注意我是如何使用像对象这样的类的(这可能是我的问题 - 在 Smalltalk 中,类是像其他所有东西一样的对象 - 我不确定它们是否在 Objective-C 中?)

我的确切问题是,当我从数组中取出一个策略对象时,我想调用该方法:

id<Counter> counter = [[MyModel availableCounters] objectAtIndex: self.index];
return [counter countFor: self];

我在 return 语句上收到警告 - 它说 -countFor: not found in protocol(因此它假设它是我想要调用类方法的实例方法)。然而,由于我的数组中的对象是类的实例,它们现在就像实例方法(或者在概念上它们应该是)。

有没有调用类方法的神奇方法?或者这只是一个坏主意,我应该只创建我的策略对象的实例(而不是使用类方法)?

【问题讨论】:

    标签: objective-c metaprogramming class-method


    【解决方案1】:

    这个

    id <Counter> counter = [[Model availableCounters] objectAtIndex:0];
    return ( [counter countFor: nil] );
    

    应该是

    Class <Counter> counter = [[Model availableCounters] objectAtIndex:0];
    return ( [counter countFor: nil] );
    

    首先,您有一个符合&lt;Counter&gt; 的实例。在第二个中,您有一个符合&lt;Counter&gt; 的类。编译器警告是正确的,因为符合&lt;Counter&gt; 的实例不会响应countFor:,只有类会。

    【讨论】:

    • 我也同意在这里使用实例比使用类感觉更好。
    • 这个答案是正确的 - 起初我没有注意到 Class 与 id 的微妙之处 - 它实际上回答了我帖子的原始标题。
    【解决方案2】:

    Objective-C 中的类确实像实例一样工作——主要的潜在区别是保留计数对它们没有任何作用。但是,您在 -availableCounters 数组中存储的不是实例(id 类型),而是具有Class 类型的类。因此,使用上面指定的 -availableCounters 定义,您需要的是:

    Class counterClass = [[MyModel availableCounters] objectAtIndex: self.index];
    return ( [counterClass countFor: self] );
    

    但是,如果您使用实例而不是类,它可能在语义上会更好。在这种情况下,您可以执行以下操作:

    @protocol Counter
    - (NSUInteger) countFor: (Model *) model;
    @end
    
    @interface CurrentListCounter : NSObject<Counter>
    @end
    
    @interface SomeOtherCounter : NSObject<Counter>
    @end
    

    那么您的模型类可以实现以下内容:

    static NSArray * globalCounterList = nil;
    
    + (void) initialize
    {
        if ( self != [Model class] )
            return;
    
        globalCounterList = [[NSArray alloc] initWithObjects: [[[CurrentListCounter alloc] init] autorelease], [[[SomeOtherCounter alloc] init] autorelease], nil];
    }
    
    + (NSArray *) availableCounters
    {
        return ( globalCounterList );
    }
    

    那么您的使用方式将与您在上面指定的完全一样:

    id<Counter> counter = [[Model availableCounters] objectAtIndex: self.index];
    return ( [counter countFor: self] );
    

    【讨论】:

    • 谢谢 Jim - 我很想看看你能把 Objective-C 推到多远。听起来,当你点击类时,它的行为开始有点不同(尽管它确实有效 - 类仍然是对象,这很好) - 所以坚持实例可能更好。评论开头的一件事 - “Class counterClass”正在避免协议,而我的类也符合“Counter”。我猜缺少的是能够正确地为类指定协议(在类方面)——比如:@interface CurrentListCounter : NSObject
    • 您也许可以使用 Class counterClass。我不知道。但这样做只会真正限制编译器不会警告的消息——ObjC 类型检查实际上只是建议性的。协议可以指定类方法,因此您不需要任何类型的 特殊语法(例如,查看 NSObject 协议)。
    • 酷 - 这就是解决方案:Class counter = [[Model availableCounters]...。这告诉编译器我有一个符合该协议的类的实例。当然,在 Obj-c 中使用类作为对象是否可取仍然是一个品味问题(共识似乎不是)
    【解决方案3】:

    事实证明它确实有效,警告不正确。

    所以问题仍然在于它是否合理(如果它们不需要任何状态,则使用类方法)?

    以及如何最好地处理警告(我喜欢无警告运行)?

    我唯一的解决方法是使用第二个协议(与第一个协议基本相同,但在实例端声明它):

    @protocol CounterInstance
    -(NSInteger) countFor: (Model *)model;
    @end
    

    在我访问计数器的地方使用它来代替(欺骗编译器):

    id<CounterInstance> counter = [[MyModel availableCounters] objectAtIndex: self.index];
    return [counter countFor: self];
    

    它有点尴尬,但确实有效。对他人的观点感兴趣吗?

    【讨论】:

    • 警告不正确。 Objective-C 的动态特性意味着消息无论如何都会发送到对象。编译器发出警告,让您知道它确实需要一个实例方法。至于您的解决方案-我认为这很难看。照吉姆说的去做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 2021-01-07
    相关资源
    最近更新 更多