Objective-C使用_Nonnull参数覆盖_Nullable参数

时间:2018-05-14 19:34:37

标签: objective-c

为什么此代码会生成以下警告?

@interface Foo : NSObject 
- (void)m:(id _Nullable)p;
@end

@interface Bar : Foo
- (void)m:(id _Nonnull)p;
@end
参数类型的

冲突的可空性说明符,'_Nonnull'与现有的说明符'_Nullable'冲突[-Wnullability]

但另一种方式不会产生警告:

@interface Foo : NSObject 
- (void)m:(id _Nonnull)p;
@end

@interface Bar : Foo
- (void)m:(id _Nullable)p;
@end

是否因为在第二种情况下我们从限制性更强到限制性更强?

1 个答案:

答案 0 :(得分:4)

这完全是因为 Liskov substitution principle

根据这个原则:

  

如果S是T的子类型,那么对象是   类型T可以用类型S的对象替换而不改变任何类型   该计划的理想属性。

在您的示例中:BarFoo的子类型。因此,您应该能够将Foo的实例替换为Bar的实例。

换句话说,想象方法

- (void)doSomethingWith:(Foo *)object {
    [object m:nil];
}

如果您传递给Foo的此方法实例 - 一切正常。但您可能无法传递给Bar的实例 - 因为您可能不会调用[Bar m:nil]

在这里你可以看到,这段代码打破了Liskov替换原则。

否则,在第二个例子中,一切都很好。

想象一下方法:

- (void)doSomethingWith:(Foo *)object {
    [object m:@""]; // _Nonnull requirement
}

如您所见,您可以传递FooBar类的此方法实例。

注意:这里需要注意的是,在Objc中(与Swift不同)这些规则并不严格,您可以轻易欺骗编译器。