为什么Objective-C类和协议可以具有相同的名称,但Swift不能,语言实现有什么区别?

时间:2018-03-11 15:17:30

标签: objective-c swift

Swift,无法编译,编译器会直接报错。

protocol Test {}

struct Test {}


// Swift compile output:

// Untitled.swift:4:8: error: invalid redeclaration of 'Test' struct Test {}

// Untitled.swift:2:10: note: 'Test' previously declared here protocol Test {}

Objective-C,可以编译成功,例如NSObject是一个类名,它也是一个协议名称

#import <Foundation/Foundation.h>

@protocol Test
@end

@interface Test
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        NSLog(@"Hello word");
    }
}
// Objective-C output
// 2018-03-11 23:14:20.341 Untitled[34921:1272761] Hello word

2 个答案:

答案 0 :(得分:5)

Objective-C和Swift有不同的名称解析方案,导致这种情况发生。

  1. 在Objective-C中,类和协议名称通常是唯一的。 (您总是可以注册一个与现有协议或类同名的新协议或类,但是您通常会收到警告,并且保证行为奇怪。)但是,类名和协议名存在于不同的名称空间中 - 类名可以影响协议名称,反之亦然。这通常没问题,因为在源代码中类和协议的引用方式不同:名为Foo的类由裸标识符Foo引用,而名为Foo的协议由@protocol(Foo)。这里没有冲突。
  2. 然而,在Swift中,不同类型之间的名称解析没有区别。协议名称与所有其他类型名称在同一名称空间中,引用名为Foo的类和名为Foo的协议之间的语法没有区别,部分导致上述错误。< / LI>

    请注意,由于Swift中名称解析的方式,enum s / struct s / class es 可以与协议具有相同的名称,并且反之亦然;名称本身在Swift中不是唯一的,但完全限定的名称是。您收到上述错误的原因实际上是因为struct Testprotocol Test都具有相同的完全限定名称:<name-of-your-module>.Test

    但是,没有什么可以阻止您在不同的模块中声明struct Testprotocol Test,因为它们具有不同的完全限定名称。例如,欢迎您添加

    struct ExpressibleByStringLiteral {}
    

    到您的代码,尽管标准库提供了该名称的协议。 ExpressibleByStringLiteral会影响标识符的其他用法,因此要引用stdlib提供的协议,您需要使用完全限定名称Swift.ExpressibleByStringLiteral

    struct ExpressibleByStringLiteral {}
    struct S1 : ExpressibleByStringLiteral {} // error: inheritance from non-protocol type 'ExpressibleByStringLiteral'
    
    struct S2 : Swift.ExpressiblyByStringLiteral {} // need to add methods to satisfy the protocol
    

    对于Swift中的所有类型都是如此 - 只要完全限定名称是唯一的,多个类型可以具有相同的名称。

答案 1 :(得分:0)

编译器可以根据上下文区分协议名称和类名称。因此,如果您知道将其用作协议以及将其用作类,就不会有歧义。

比方说,我们有一个符合Car协议的Car类。

@protocol Car <NSObject>
- (BOOL)canRun;
@end

@interface Car : NSObject <Car>
@end

@implementation Car
- (NSString *)description {
    return [NSString stringWithFormat:@"im a car"];
}
- (BOOL)canRun {
    return YES;
}
@end

您可以使用Car作为协议,编译器会知道。

/* compiler tasks Car as a protocol*/
// 1
id<Car> car;

// 2
[obj comformsToProtocol: @protocol(Car)];

// 3
@interface Truck : NSObject <Car>
@end

// 4
@protocol AnotherProtocol <Car>
@end

您还可以将Car用作类,编译器也将知道它。

/* compiler tasks Car as a class*/
// 1 
Car *car = [Car new];

// 2
[Car someClassMethod];

// 3
@interface Truck : Car
@end