Skip to content

Commit bcf63d9

Browse files
committed
Add protocols hint
1 parent 72b4f63 commit bcf63d9

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
layout: post
3+
author: rafa
4+
title: "Optional protocol methods without @objc"
5+
date: 2016-08-21 22:36:49 +0200
6+
comments: false
7+
categories:
8+
---
9+
10+
Yesterday I was watching a [talk](https://realm.io/news/altconf-nikita-lutsenko-objc-swift-interoperability/) from Nikita Lutsenko about Swift and Objective-C Interoperability. At some point he states:
11+
12+
>
13+
What Objective-C protocols are in Swift is very special. There is so much that was added specifically for it. It’s also weaker and not Swift-y, even though you can use `optional`. So, `optional` is not supported on Swift protocols, unless they are exported into Objective-C, because that time, they become Objective-C protocols.
14+
There is also no ability to talk to extensions of these protocols. Either they are concrete extensions, with no way you can talk to a protocol and build things like protocol extensions, which we all love and use in Swift. So they’re very limited altogether.
15+
If you actually are writing Swift code, please don’t use these, they make everyone’s life bad as well as they could not be used, say, in a Linux environment, where you don’t have Objective-C runtime.
16+
17+
He's right! When you annotate a protocol with `@objc`, LLVM will generate a bunch of extra things: an `isa` pointer, runtime sections like: `__objc_imageinfo`, `__objc_classref`, etc. It seems that it's casting the protocol into a `NSObject`, and maybe it will have a performance penalty. Also `@objc` protocols can not be used with struct and enums, just class types.
18+
19+
The following assembly is for `@objc protocol`:
20+
21+
```llvm
22+
;
23+
; @protocol _TtP9Protocols8Protocol_ {
24+
; -method
25+
; }
26+
00000001002afcb0 dq 0x0 ; isa, XREF=0x1002abaa0, 0x1002b03c8
27+
00000001002afcb8 dq 0x1002782f0 ; name, "_TtP9Protocols8Protocol_"
28+
00000001002afcc0 dq 0x0 ; protocols
29+
00000001002afcc8 dq 0x1002afd00 ; instance methods
30+
00000001002afcd0 dq 0x0 ; class methods
31+
00000001002afcd8 dq 0x0 ; optional instanceMethods
32+
00000001002afce0 dq 0x0 ; optional class methods
33+
00000001002afce8 dq 0x0 ; instance properties
34+
00000001002afcf0 dd 0x00000050 ; size
35+
00000001002afcf4 dd 0x00000001 ; flags
36+
00000001002afcf8 db 0x20 ; ' '
37+
00000001002afcf9 db 0xfd ; '.'
38+
00000001002afcfa db 0x2a ; '*'
39+
00000001002afcfb db 0x00 ; '.'
40+
00000001002afcfc db 0x01 ; '.'
41+
00000001002afcfd db 0x00 ; '.'
42+
00000001002afcfe db 0x00 ; '.'
43+
00000001002afcff db 0x00 ; '.'
44+
00000001002afd00 db 0x18 ; '.' ; XREF=0x1002afcc8
45+
00000001002afd01 db 0x00 ; '.'
46+
00000001002afd02 db 0x00 ; '.'
47+
00000001002afd03 db 0x00 ; '.'
48+
00000001002afd04 db 0x01 ; '.'
49+
00000001002afd05 db 0x00 ; '.'
50+
00000001002afd06 db 0x00 ; '.'
51+
00000001002afd07 db 0x00 ; '.'
52+
00000001002afd08 db 0x0a ; '.'
53+
00000001002afd09 db 0xf2 ; '.'
54+
00000001002afd0a db 0x26 ; '&'
55+
00000001002afd0b db 0x00 ; '.'
56+
00000001002afd0c db 0x01 ; '.'
57+
00000001002afd0d db 0x00 ; '.'
58+
00000001002afd0e db 0x00 ; '.'
59+
00000001002afd0f db 0x00 ; '.'
60+
00000001002afd10 db 0xe0 ; '.'
61+
00000001002afd11 db 0x82 ; '.'
62+
00000001002afd12 db 0x27 ; '''
63+
00000001002afd13 db 0x00 ; '.'
64+
00000001002afd14 db 0x01 ; '.'
65+
00000001002afd15 db 0x00 ; '.'
66+
00000001002afd16 db 0x00 ; '.'
67+
00000001002afd17 db 0x00 ; '.'
68+
00000001002afd18 db 0x00 ; '.'
69+
00000001002afd19 db 0x00 ; '.'
70+
00000001002afd1a db 0x00 ; '.'
71+
00000001002afd1b db 0x00 ; '.'
72+
00000001002afd1c db 0x00 ; '.'
73+
00000001002afd1d db 0x00 ; '.'
74+
00000001002afd1e db 0x00 ; '.'
75+
00000001002afd1f db 0x00 ; '.'
76+
00000001002afd20 db 0xe0 ; '.'
77+
00000001002afd21 db 0x82 ; '.'
78+
00000001002afd22 db 0x27 ; '''
79+
00000001002afd23 db 0x00 ; '.'
80+
00000001002afd24 db 0x01 ; '.'
81+
00000001002afd25 db 0x00 ; '.'
82+
00000001002afd26 db 0x00 ; '.'
83+
00000001002afd27 db 0x00 ; '.'
84+
;
85+
; Section __objc_selrefs
86+
;
87+
; Range 0x1002afd28 - 0x1002b0360 (1592 bytes)
88+
; File offset 2817320 (1592 bytes)
89+
; Flags : 0x10000005
90+
;
91+
00000001002afd28 dq 0x10026daa6 ; @selector(hash), "hash", XREF=0x1000008e8, -[NSObject hashValue]+4, __TFE10ObjectiveCCSo8NSObjectg9hashValueSi+4, __TFE10FoundationSSg4hashSi+15, _swift_stdlib_NSStringNFDHashValue+27, _swift_stdlib_NSStringASCIIHashValue+10
92+
00000001002afd30 dq 0x10026daab ; @selector(isEqual:), "isEqual:", XREF=__TTWCSo8NSObjects9Equatable10ObjectiveCZFS0_oi2eefTxx_Sb+16, __TZF10ObjectiveCoi2eeFTCSo8NSObjectS0__Sb+16, _swift_stdlib_NSObject_isEqual+21
93+
00000001002afd38 dq 0x10026dab4 ; @selector(hashValue), "hashValue", XREF=__TTWCSo8NSObjects8Hashable10ObjectiveCFS0_g9hashValueSi+7
94+
00000001002afd40 dq 0x10026dabe ; @selector(description), "description", XREF=__TTWC
95+
; assembly code goes by
96+
```
97+
98+
But when we declare just `protocol`, here's what you end-up with:
99+
100+
```llvm
101+
0000000100276b90 db "_TtP9Protocols8Protocol_", 0
102+
;
103+
; Section __objc_methname
104+
;
105+
; Range 0x100276ba9 - 0x10027830d (5988 bytes)
106+
; File offset 2583465 (5988 bytes)
107+
; Flags : 0x00000002
108+
;
109+
0000000100276ba9 db "hash", 0 ; XREF=0x100000210, 0x1002afcf0
110+
0000000100276bae db "isEqual:", 0 ; XREF=0x1002afcf8
111+
0000000100276bb7 db "hashValue", 0 ; XREF=0x1002afd00
112+
0000000100276bc1 db "description", 0 ; XREF=0x1002afd08
113+
; assembly code goes by
114+
```
115+
116+
Much more assembly, right?
117+
118+
So, let's think this thoroughly, shall we?
119+
`optional` means that a function may or may not exist.
120+
In Objective-C, before passing a message, we have to check for `respondsToSelector`, otherwise it will crash at runtime.
121+
In Swift, we just call the function, followed by a `?`, all safe, no crashes.
122+
123+
But I think that there's a more Swifty way of doing that, by using protocol extensions, take a look:
124+
125+
```swift
126+
protocol Protocol: class {
127+
func requiredMethodOne()
128+
func requiredMethodTwo()
129+
}
130+
131+
extension Protocol {
132+
func optionalMethodOne() {}
133+
func optionalMethodTwo() {}
134+
}
135+
```
136+
137+
Now, if someone calls `optionalMethodOne()`, nothing happens, due to it's empty implementation. We also mitigate the risk of someone, accidentally, forcing unwrap an optional functional. Not to mention the fact that, now, it's not constraint to a specific type!
138+
139+
Protocol extende all the things!
140+

0 commit comments

Comments
 (0)