Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 698
JAL Avatar asked May 25 '16 16:05

JAL


People also ask

What is NSObjectProtocol Swift?

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.

What is NSObjectProtocol?

The group of methods that are fundamental to all Objective-C objects.

Can we create instance of protocol in Swift?

You can not create an instance of protocol.

Can Objective-C class conform to Swift 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.


2 Answers

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.

like image 122
Brian Nickel Avatar answered Oct 15 '22 01:10

Brian Nickel


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.

like image 38
JAL Avatar answered Oct 15 '22 01:10

JAL