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
.
The usual equivalent of NSObject<Protocol> in Swift is simply Protocol . Typically, this protocol is declared as a class protocol to guarantee that it will be adopted by a class. If you also need the NSObject protocol methods (such a respondsToSelector: , then make Protocol adopt NSObjectProtocol.
The group of methods that are fundamental to all Objective-C objects.
You can not create an instance of protocol.
Mitrenegades solution is to use an objective-c protocol, is one way, but if you want a swift protocol, then the other would be to refactor the code so as to not use the objective-c class directly, but instead use the protocol (e.g. some protocol based factory pattern). Either way may be appropriate for your purposes.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With