声明一个协议 @objc 和让它在纯 Swift 中符合 NSObjectProtocol 有什么区别?

What is the difference between declaring a protocol @objc and having it conform to NSObjectProtocol in pure Swift?

考虑两个 Swift 协议:

@objc protocol SomeProtocol { }

protocol SomeOtherProtocol: NSObjectProtocol { }

声明 Swift 协议 @objc 或使其符合 NSObjectProtocol 有什么区别?我知道任何不 @objc 的协议都不会桥接到 Objective-C,但是这两个声明在纯 Swift 应用程序中有什么区别?据我了解,@objc 应该通过顶级 SwiftObject.

使 SomeProtocol 符合 NSObjectProtocol

我发现的一个区别是,使协议符合 NSObjectProtocol 会将 Objective-C 符号信息加载到已编译的 Swift 二进制文件中。请参阅下面生成的程序集:

l__PROTOCOL_NSObject:
    .quad   0
    .quad   L___unnamed_2
    .quad   0
    .quad   l__PROTOCOL_INSTANCE_METHODS_NSObject
    .quad   0
    .quad   l__PROTOCOL_INSTANCE_METHODS_OPT_NSObject
    .quad   0
    .quad   l__PROTOCOL_PROPERTIES_NSObject
    .long   80
    .long   0
    .quad   l__PROTOCOL_METHOD_TYPES_NSObject

    .private_extern l_OBJC_LABEL_PROTOCOL_$_NSObject
    .section    __DATA,__objc_protolist,coalesced,no_dead_strip
    .globl  l_OBJC_LABEL_PROTOCOL_$_NSObject
    .weak_definition    l_OBJC_LABEL_PROTOCOL_$_NSObject
    .align  3
l_OBJC_LABEL_PROTOCOL_$_NSObject:
    .quad   l__PROTOCOL_NSObject

    .private_extern l_OBJC_PROTOCOL_REFERENCE_$_NSObject
    .section    __DATA,__objc_protorefs,coalesced,no_dead_strip
    .globl  l_OBJC_PROTOCOL_REFERENCE_$_NSObject
    .weak_definition    l_OBJC_PROTOCOL_REFERENCE_$_NSObject
    .align  3
l_OBJC_PROTOCOL_REFERENCE_$_NSObject:
    .quad   l__PROTOCOL_NSObject

    .section    __DATA,__const
    .align  3
l___unnamed_3:
    .quad   1
    .quad   l__PROTOCOL_NSObject

    .globl  __TMp3obj10MyProtocol
    .align  3
__TMp3obj10MyProtocol:
    .quad   0
    .quad   L___unnamed_1
    .quad   l___unnamed_3
    .quad   0
    .quad   0
    .quad   0
    .quad   0
    .quad   0
    .long   72
    .long   5

# ...

# __swift_FORCE_LOAD_ of linked libraries

__swift_FORCE_LOAD_$_swiftCoreGraphics_$_obj:
    .quad   __swift_FORCE_LOAD_$_swiftCoreGraphics

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(isEqual:)":
    .asciz  "isEqual:"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_4:
    .asciz  "c24@0:8@16"

L___unnamed_5:
    .asciz  "hash"

L___unnamed_6:
    .asciz  "Tq,N,R"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(hash)":
    .asciz  "hash"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_7:
    .asciz  "q16@0:8"

L___unnamed_8:
    .asciz  "superclass"

L___unnamed_9:
    .asciz  "T#,N,R"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(superclass)":
    .asciz  "superclass"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_10:
    .asciz  "#16@0:8"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(class)":
    .asciz  "class"

"L_selector_data(self)":
    .asciz  "self"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_11:
    .asciz  "@16@0:8"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(performSelector:)":
    .asciz  "performSelector:"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_12:
    .asciz  "^@24@0:8:16"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(performSelector:withObject:)":
    .asciz  "performSelector:withObject:"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_13:
    .asciz  "^@32@0:8:16@24"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(performSelector:withObject:withObject:)":
    .asciz  "performSelector:withObject:withObject:"

    .section    __TEXT,__cstring,cstring_literals
    .align  4
L___unnamed_14:
    .asciz  "^@40@0:8:16@24@32"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(isProxy)":
    .asciz  "isProxy"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_15:
    .asciz  "c16@0:8"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(isKindOfClass:)":
    .asciz  "isKindOfClass:"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_16:
    .asciz  "c24@0:8#16"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(isMemberOfClass:)":
    .asciz  "isMemberOfClass:"

"L_selector_data(conformsToProtocol:)":
    .asciz  "conformsToProtocol:"

    .section    __TEXT,__cstring,cstring_literals
    .align  4
L___unnamed_17:
    .asciz  "c24@0:8@\"Protocol\"16"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(respondsToSelector:)":
    .asciz  "respondsToSelector:"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_18:
    .asciz  "c24@0:8:16"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(retain)":
    .asciz  "retain"

"L_selector_data(release)":
    .asciz  "release"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_19:
    .asciz  "v16@0:8"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(autorelease)":
    .asciz  "autorelease"

"L_selector_data(retainCount)":
    .asciz  "retainCount"

"L_selector_data(zone)":
    .asciz  "zone"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_20:
    .asciz  "^v16@0:8"

L___unnamed_21:
    .asciz  "description"

    .align  4
L___unnamed_22:
    .asciz  "T@\"NSString\",N,R"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(description)":
    .asciz  "description"

    .section    __TEXT,__cstring,cstring_literals
    .align  4
L___unnamed_23:
    .asciz  "@\"NSString\"16@0:8"

    .align  4
L___unnamed_24:
    .asciz  "debugDescription"

    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(debugDescription)":
    .asciz  "debugDescription"

    .section    __TEXT,__cstring,cstring_literals
L___unnamed_2:
    .asciz  "NSObject"

    .section    __DATA,__objc_const
    .align  3
l__PROTOCOL_INSTANCE_METHODS_NSObject:
    .long   24
    .long   19
    .quad   "L_selector_data(isEqual:)"
    .quad   L___unnamed_4
    .quad   0
    .quad   "L_selector_data(hash)"
    .quad   L___unnamed_7
    .quad   0
    .quad   "L_selector_data(superclass)"
    .quad   L___unnamed_10
    .quad   0
    .quad   "L_selector_data(class)"
    .quad   L___unnamed_10
    .quad   0
    .quad   "L_selector_data(self)"
    .quad   L___unnamed_11
    .quad   0
    .quad   "L_selector_data(performSelector:)"
    .quad   L___unnamed_12
    .quad   0
    .quad   "L_selector_data(performSelector:withObject:)"
    .quad   L___unnamed_13
    .quad   0
    .quad   "L_selector_data(performSelector:withObject:withObject:)"
    .quad   L___unnamed_14
    .quad   0
    .quad   "L_selector_data(isProxy)"
    .quad   L___unnamed_15
    .quad   0
    .quad   "L_selector_data(isKindOfClass:)"
    .quad   L___unnamed_16
    .quad   0
    .quad   "L_selector_data(isMemberOfClass:)"
    .quad   L___unnamed_16
    .quad   0
    .quad   "L_selector_data(conformsToProtocol:)"
    .quad   L___unnamed_4
    .quad   0
    .quad   "L_selector_data(respondsToSelector:)"
    .quad   L___unnamed_18
    .quad   0
    .quad   "L_selector_data(retain)"
    .quad   L___unnamed_11
    .quad   0
    .quad   "L_selector_data(release)"
    .quad   L___unnamed_19
    .quad   0
    .quad   "L_selector_data(autorelease)"
    .quad   L___unnamed_11
    .quad   0
    .quad   "L_selector_data(retainCount)"
    .quad   L___unnamed_7
    .quad   0
    .quad   "L_selector_data(zone)"
    .quad   L___unnamed_20
    .quad   0
    .quad   "L_selector_data(description)"
    .quad   L___unnamed_11
    .quad   0

    .align  3
l__PROTOCOL_INSTANCE_METHODS_OPT_NSObject:
    .long   24
    .long   1
    .quad   "L_selector_data(debugDescription)"
    .quad   L___unnamed_11
    .quad   0

    .align  3
l__PROTOCOL_PROPERTIES_NSObject:
    .long   16
    .long   4
    .quad   L___unnamed_5
    .quad   L___unnamed_6
    .quad   L___unnamed_8
    .quad   L___unnamed_9
    .quad   L___unnamed_21
    .quad   L___unnamed_22
    .quad   L___unnamed_24
    .quad   L___unnamed_22

    .align  3
l__PROTOCOL_METHOD_TYPES_NSObject:
    .quad   L___unnamed_4
    .quad   L___unnamed_7
    .quad   L___unnamed_10
    .quad   L___unnamed_10
    .quad   L___unnamed_11
    .quad   L___unnamed_12
    .quad   L___unnamed_13
    .quad   L___unnamed_14
    .quad   L___unnamed_15
    .quad   L___unnamed_16
    .quad   L___unnamed_16
    .quad   L___unnamed_17
    .quad   L___unnamed_18
    .quad   L___unnamed_11
    .quad   L___unnamed_19
    .quad   L___unnamed_11
    .quad   L___unnamed_7
    .quad   L___unnamed_20
    .quad   L___unnamed_23
    .quad   L___unnamed_23

    .no_dead_strip  __TMp3obj10MyProtocol

当声明协议@objc时,这些符号没有加载,我认为这项工作被转移到Swift模块的Objective-C头文件。

MyApp-Swift.h

SWIFT_PROTOCOL("_TtP15MyApp12SomeProtocol_")
@protocol SomeProtocol
@end

每个协议生成的程序集之间的完整差异 here

存在差异,但可能有些细微差别。简而言之,@objc 为您提供了 optional,您不需要并且不适用于结构。另一方面,NSObjectProtocol 本质上只是将实现限制为 NSObject.

的子classes

将协议注释为 @objc 意味着它已在 Objective-C 运行时注册,允许协议具有运行时特定的功能,即可选要求。这也意味着该协议不能用在结构上,但它可以用在 any class 上,同样,包含它的字典和数组可以桥接到 NSDictionary 和 NSArray。从纯粹的 swift 角度来看,您可能没有理由 需要 这样做,因为该功能已在很大程度上被协议扩展所取代。

另一方面,当协议扩展 NSObjectProtocol 时,它仍然可以用在结构上 但是 该结构必须实现所有预期的方法NSObjectProtocol。这可能是一项艰巨的任务。从实际的角度来看,它实际上只是迫使您使实现 SomeOtherProtocol 的 class 成为链上某处 NSObject 的子class。