简体   繁体   中英

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

Consider two Swift protocols:

@objc protocol SomeProtocol { }

protocol SomeOtherProtocol: NSObjectProtocol { }

What is the difference between declaring a Swift protocol @objc or having it conform to NSObjectProtocol ? I know that any protocol that does not @objc will not be bridged to Objective-C, but what is the difference between these two declarations in a pure Swift application? As I understand it, @objc should conform SomeProtocol to NSObjectProtocol through the top-level SwiftObject .

There are differences, but they can be a bit nuanced. In a nutshell, @objc gives you optional which you shouldn't need and doesn't work with structs. NSObjectProtocol on the other hand essentially just constrains implementations to be subclasses of NSObject .

Annotating a protocol as @objc means it is registered with the Objective-C runtime, allowing the protocol to have runtime specific features, namely optional requirements. It also means that the protocol cannot be used on a struct but it could be used on any class and likewise dictionaries and arrays holding it can be bridged to NSDictionary and NSArray. From a pure-swift perspective, there's probably no reason you would need to do, since that feature has largely been supplanted with protocol extensions.

When a protocol extends NSObjectProtocol , on the other hand, it can still be used on a struct but that struct will have to implement all the methods expected of NSObjectProtocol . That can be a daunting task. From a practical perspective, it really just forces you to make classes implementing SomeOtherProtocol be subclasses of NSObject somewhere up the chain.

One difference I've found is that conforming a protocol to NSObjectProtocol will load Objective-C symbol information into your compiled Swift binary. See the generated assembly below:

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

When declaring the protocol @objc , these symbols aren't loaded, and I think this work is transferred to the Swift module's Objective-C header.

MyApp-Swift.h

SWIFT_PROTOCOL("_TtP15MyApp12SomeProtocol_")
@protocol SomeProtocol
@end

Full diff between the generated assembly of each protocol here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM