【问题标题】:Objective C class method == C++ constructor?目标 C 类方法 == C++ 构造函数?
【发布时间】:2014-03-13 18:04:20
【问题描述】:

我知道一些 C++,现在我也将尝试学习 Objective-C。我的书简单地提到了类方法,并说类方法是可以发送给类的消息,通常用于创建类的实例。

这是否意味着类方法有点像 C++ 中的构造函数?就我而言,我有一个带有 Deck 类的纸牌游戏(目前在 C++ 中)。当我创建它的一个实例时,我会这样做:

Deck deckWithCards(52);  // A deck with 52 cards with values
Deck deckWithoutCards;   // An empty deck

如果我要在 Objective-C 中做同样的事情,这样做是否被认为是好的“风格”:

Deck *deckWithCards = [Deck newDeck:52]; // A deck with 52 cards with values
Deck *deckWithoutCards = [Deck newDeck]; // An empty deck

我是否正确理解了类方法?类方法还有其他用途吗?

【问题讨论】:

  • ObjC 类方法和 C++ 静态成员函数有时用于创建“工厂”函数,这就是您所描述的。工厂函数是一个函数,其目的是为您提供一个对象......它是对象工厂。当涉及构造函数时,工厂函数会调用它(可能基于您提供的输入)。在 C++ 中,直接调用构造函数类似于使用工厂函数的模式,但由于构造函数不分配对象,所以它不是工厂。
  • 你可以这样做,但是请不要这样做!工厂方法作为快捷方式很好,但通常您应该使用[[object alloc] init] 语法。如果你想这样缩短,你可以使用已经为你提供的类方法new。不要重新发明轮子。

标签: c++ objective-c class methods constructor


【解决方案1】:

这是否意味着类方法有点像 C++ 中的构造函数?

它们更像是静态成员函数。例如,您可以指定类方法的返回类型。然而,ObjC 的类方法使用动态调度。

初始化器更像是一个构造器。

如果我要在 Objective-C 中做同样的事情,这样做是否被认为是好的“风格”:……

真的,你应该分解这里发生的事情。在有意义的地方使用便利构造函数并没有错。通常,您不会费心声明一个便利的构造函数,而是使用对象的初始化程序自定义初始化(这些是带有前缀 -init… 的实例方法)。

与其使用+newDeckWithNCards:+newDeck,不如从定义或覆盖指定的初始化程序开始:

- (instancetype)init;
- (instancetype)initWithNCards:(NSUInteger)pNCards;

在某些情况下,提供便捷构造函数会很方便,有时在实现类集群时您会更喜欢类方法或工厂(很多人从不这样做)。

在大多数情况下,初始化程序就足够了,但在某些情况下使用便利构造函数当然也不错。

类方法可以用于更多。再一次,将它们视为也具有动态调度的静态成员函数(因此NSString 方法可以通过覆盖在其类方法中执行与NSMutableString 不同的实现,这是在运行时确定的)。

【讨论】:

    【解决方案2】:

    类方法是 C++ 中所谓的“静态成员函数”。

    Objective-C 的“命名构造函数”习语有时也用于 C++:

    class Deck
    {
    public:
        static Deck* newDeck(int cards = 0);
        // ...
    };
    
    // ...
    
    Deck* deckWithCards = Deck::newDeck(52);
    Deck* deckWithoutCards = Deck::newDeck();
    

    但在存在可重载构造函数的情况下,它就没有那么有用了。
    它也不能很好地与 RAII 配合使用。

    【讨论】:

    • 您可以通过返回值或智能指针对 RAII 更加友好。
    【解决方案3】:

    类方法与 C++ 中的静态方法最相似。然而它们并不相同。在 C++ 中,类在运行时不存在。 Objective-C 中的类是真实的对象,您可以在其上调用方法(用 Objective-C 术语向其发送消息)。所以在 Objective-C 中你可以做这样的事情:

    Class classObj = [NSString class];
    NSString *instanceObj = [classObj stringWithFormat:@"Hi I am %d years old", 36];
    

    类似于做:

    NSString *instanceObj = [NSString stringWithFormat:@"Hi I am %d years old", 36];
    

    您可能想知道这样做的目的是什么,它免费为您提供Factory pattern。你可以例如使用它从插件加载一个类,并在加载的类上调用类方法,而不知道它是哪个类。

    关于你的例子。在 Objective-C 中,通常将分配与初始化分开,所以:

    Deck *deckWithCards = [Deck newDeck:52];
    

    推荐。相反,你应该把它写成:

    Deck *deckWithCards = [[Deck alloc] initWithNoCards:52];
    

    回到你原来的问题是类方法与构造函数相同。正如我所展示的,它们不是真的,尽管我能理解你为什么要进行比较,因为它们有一些重叠的用法。

    但在 Objective-C 中确实没有像构造函数这样的东西。 C++ 和 Java 中的构造函数是语言的一部分,具有非常特定的用途。如果不调用 C++ 和 Java 中的构造函数,就无法创建对象。在 Objective-C 中,这并没有硬连线到语言中。这都是关于约定的。 allocinit 只是约定。同样,您可以使用类方法来做任何事情。他们不必创建任何对象。

    如果您想详细了解它的工作原理,我推荐Mike Ash's article about how you can implement NSObject

    【讨论】:

    • 您链接到的文章不错,但对于只是想了解静态方法的人来说可能有点太复杂了。
    • @SevenBits 是的,这可能太多了,但我认为这取决于人。对于快速回答,它没有帮助。但是对于真正想了解为什么静态方法与对象创建上下文中的类方法不同的人来说,它可能非常有用。
    【解决方案4】:

    不,这不是一个好习惯。你可以这样做,但请不要这样做!工厂方法作为快捷方式很好,但通常您应该使用[[class alloc] init] 语法。如果你想这样缩短,你可以使用已经为你提供的类方法new。不要重新发明轮子。

    我已经看到 API 和程序员按照您刚才所做的方式做事,我立即知道它们来自像 C++ 这样的语言很常见,而且我知道他们不是“原生”Objective-C 程序员。重要的是要记住,类 C 语言非常灵活,并且仅仅因为您可以做某事并不意味着您应该这样做。

    现在工厂方法,就像你所描述的那样,在某些情况中很有用,但绝对不是你的例子。例如,工厂方法在NSString 中的stringWithFormat: 之类的地方使用,因为否则很难做到,而且默认范式会让人头疼,但它们是例外,而不是规则。

    只要我的两分钱。

    【讨论】:

    • 在我的情况下,我应该如何创建甲板?是:Deck *myDeck = [Deck new];,然后是[myDeck setCards:52];
    • 是的,或者:Deck *myDeck = [[Deck alloc] initWithCards:52]; 这是最好的,最像 Cocoa 的方式。
    猜你喜欢
    • 2016-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-27
    • 1970-01-01
    • 2015-12-01
    • 1970-01-01
    • 2011-09-25
    相关资源
    最近更新 更多