【问题标题】:Missing sentinel in method dispatch方法调度中缺少哨兵
【发布时间】:2012-01-02 17:10:38
【问题描述】:

我想创建NSMutableArray 的子类,需要重写-initWithObjects: 方法。

但是如何调用[super xxx];?

- (id) initWithObjects:(id)firstObj, ... {
    [super initWithObjects:firstObj]; // Error: Missing sentinel in method dispatch
    // Error: The result of a delegate init call must be immediately returned or assigned to "self"

}

谢谢。

【问题讨论】:

    标签: objective-c cocoa nsarray subclass


    【解决方案1】:

    那么“missing sentinel”消息是指丢失的 nil 终止。事实上,根据font-of-all-knowledge-Wikipedia:

    在 Objective-C 中终止可变长度参数列表的 nil 的名称

    also: Sentinel 节点,一个对象,表示数据结构的结束 也:哨兵值,用于终止循环的值 另外:在 Bisync 等网络协议中,标记值指示帧的开始和结束位置

    【讨论】:

      【解决方案2】:

      你不能。如the documentation for NSArray 中所述:

      您可能希望为您的子类实现一个初始化程序,即 适合子类管理的后备存储。 NSArray 类没有指定的初始化程序,因此您的初始化程序需要 只调用superinit 方法。 NSArray 类采用 NSCopying、NSMutableCopying 和 NSCoding 协议;如果你想 通过复制或编码创建的您自己的自定义子类的实例, 覆盖这些协议中的方法。

      因此您可以分配self = [super init]; 并将初始化程序中的对象添加到结果对象中。事实上,由于NSArray 的实现方式,调用任何-initWith… 方法都可能返回一个不同 NSArray 子类的实例。

      请注意,文档还讨论了子类化 NSArray 的替代方案,这些替代方案可能更容易、更可靠或以其他方式更好。

      【讨论】:

        【解决方案3】:

        子类化NSArray/NSMutableArray 不像子类化大多数类那样工作。 NSArray 是一个类集群,请参阅NSArray 文档中的subclassing notes

        现在,对于您的具体问题,子类化 va_list 方法有点棘手,有很多方法可以处理这个问题。 “最安全”的方法是将您的va_list 处理为NSArray,然后将其传递给另一种处理您想要的任何方法的方法。另一种可移植性稍差、更骇人听闻的方法是在堆栈上创建一个新的va_list 列表以供通过。

            id __unsafe_unretained * stack = (typeof(stack))calloc(numOfObjects, sizeof(id));
            //filloutStack
            [super initWithObjects:*stack, nil];
            free(stack);
        

        【讨论】:

          【解决方案4】:

          子类化 Apples Collection 类并不难——如果您使用一个小技巧(另请参阅:cocoawithlove)。

          子类是面向对象设计中的“is-a”关系。但也有“has-a”关系,即包装器。

          如果您尝试使用纯 is-a 关系创建 NSArray 的子类,我想这会有点困难,因为您必须进行 C 级内存管理。

          但是如果你添加了一个has-a关系——或者:创建一个包装器——同时,你可以很容易地进行子类化:只要让你的自定义数组类有一个普通的NSArray的成员。现在通过将调用转发到成员对象来覆盖其方法。我展示了这个in this post, where I just add objects, that pass a certain test

          但是你会看到,我没有正确实现你所说的方法,但是我提出了一个错误。原因是:该方法是可变参数方法,可以传入可变数量的对象——要处理这个,你必须做一些工作。 cocoawithlove has an great article about it.

          对你来说——如果使用那个有技巧的话——它可能看起来像

          - (id) initWithObjects:(id)firstObj, ... {
          
              if (self = [super init]) {
                  _realArray = [[NSMutableArray alloc] initWithCapacity:1];
              }
          
              va_list args;
              va_start(args, firstObj);
              for (id obj = firstObj; obj != nil; obj = va_arg(args, id))
              {
                  [self.realArray addObject:obj];
              }
              va_end(args);
              return self;
          }
          

          【讨论】:

            【解决方案5】:

            试试

            self = [super initWithObjects:firstObj,nil];
            

            【讨论】:

            • 你解决了委托问题,但仍然有丢失哨兵的警告...
            • 我试过self = [super initWithObjects:firstObj, nil];,警告消失了,但是调用这个子类时数组里面的对象会丢失吗?
            • 是的,很抱歉,您必须在末尾添加 nil。对不起,我不明白你的问题...
            • @BenLu:请参阅 jlehr 的回答和我对此的评论。这会起作用,但它只会(尝试)用第一个对象初始化数组,而不是任何其他对象。充其量,您最终会得到一个包含不超过一个对象的数组,而不管传递了多少个对象。
            • 查看我对 va_list-aware 实现的回答。
            猜你喜欢
            • 1970-01-01
            • 2011-01-25
            • 2020-11-09
            • 1970-01-01
            • 2012-12-16
            • 1970-01-01
            • 2020-03-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多