Objective-C方法签名:参数类型在声明和实现之间可能有所不同吗?

时间:2015-08-14 22:37:59

标签: ios objective-c

我可以在参数类型为@interface的{​​{1}}中声明一个方法:

NSString*

在实施过程中,它是- (id) initWithString:(NSString*)str;

NSNumber*

有关完整示例,请参阅下面的代码。在调用- (id) initWithString:(NSNumber*)str 时,输出为[Work test],因此传入的a.x = Hi已经通过,人们可以看到"正确"调用NSString*方法。

为什么编译器接受此代码?

当参数类型不同时,我可以让编译器抱怨吗?

引用Apple的文档Defining Classes

  

唯一的要求是签名匹配,这意味着您必须保持方法的名称以及参数和返回类型完全相同。

我的测试代码:

initWithString

我添加了@interface ClassA : NSObject @property (strong, nonatomic) NSNumber *x; - (id) initWithString:(NSString*)str; - (void) feed:(NSString*)str; @end @implementation ClassA - (id) initWithString:(NSNumber*)str { self = [super init]; if (self) { self.x = str; } return self; } - (void) feed:(NSNumber*)str { self.x = str; } @end @implementation Work + (void) test { ClassA *a = [[ClassA alloc] initWithString:@"Hi"]; NSLog(@"a.x = %@", a.x); } @end 方法,看是否是"特殊"到feed - 类似方法,但编译器也没有抱怨。 (在Yosemite / Xcode 6.4 / iOS8.4模拟器上运行。)

PS:如果我没有使用正确的条款,请纠正我: - )

3 个答案:

答案 0 :(得分:2)

  

参数类型不同时,我可以让编译器抱怨吗?

有一个警告,您可以通过在标题中包含以下行来激活:

#pragma clang diagnostic error "-Wmethod-signatures"

您还可以将-Wmethod-signatures放入项目"其他警告标志" Xcode构建设置为整个项目激活它。

我真的不明白为什么Apple默默无闻地激活这样的有用警告。

我几乎每个项目的标准模式都是将-Weverything放入"其他警告标志"。这激活了clang提供的所有警告。

由于某些警告有点过于迂腐或者没有提供我的编码风格,因此我会在弹出时单独停用不需要的警告类型。

答案 1 :(得分:0)

In Objective-C a method is defined as a string (known as a selector) in the form of doSomethingWithParam:anotherParam:. Or in your case it will be initWithString:. Note there's no parameter types in these strings. One side-effect of defining methods like this is that Objective-C, unlike Java or C++ doesn't allow overloading operators by just changing the parameter type. Another side-effect is the behavior you observed.

EDIT: Additionally, it appears that the compiler does not look at the implementation at all when checking method calls, just the interface. Proof: declare a method in a header, don't specify any implementation for that method, and call this method from your code. This will compile just fine, but of course you'll get an "unrecognized selector" exception when you run this code.

It'd be great if someone could provide a nice explanation of the default compiler behavior.

答案 2 :(得分:0)

我发现你发现的引用声明param和return类型与方法签名的唯一性有关,我对此感到惊讶。重新阅读,我认为您发现了一个错误in the doc

在接口中定义参数类型将为未传递该类型的调用者生成警告(或将参数强制转换为该类型),无论实现如何。更改实现中的参数类型就像在方法中强制转换参数一样。没有错,甚至没有警告的原因。只要不同的类型与声明的类型共享方法(多态或继承)。

换句话说,通过示例重述......

以下将导致编译器错误,证明不同的param类型与编译器没有区别(返回类型也是如此)...

// .h
- (void)foo:(NSString *)str;

// .m
- (void)foo:(NSString *)str {    
    NSLog(@"called foo %@", [str class]);
}

- (void)foo:(NSNumber *)str {    <----- duplicate declaration error
}

以下内容不会导致编译器警告,错误或运行时错误......

// .h
- (void)foo:(NSString *)str;

// .m
- (void)foo:(NSNumber *)str {
    // everything implements 'class', so no problem here
    NSLog(@"called foo %@", [str class]);
}

以下内容与前面的例子完全相同......

// .h
- (void)foo:(NSString *)str;

// .m
- (void)foo:(NSString *)str {
    NSNumber *number = (NSNumber *)str;
    NSLog(@"called foo %@", [number class]);
}

以下将不会导致警告,但会产生运行时错误,因为我们通过调用传递类型未实现的方法来滥用强制转换(假设调用者使用字符串调用,如接口所示)...

// .h
- (void)foo:(NSString *)str;

// .m
- (void)foo:(NSNumber *)str {
    NSLog(@"does str equal 2? %d", [str isEqualToNumber:@2]); <--- crash!
}

所有上述内容都与实践中的直觉和行为相符,而不是文档中的那段话。有趣的发现!