Date
Nov. 21st, 2024
 
2024年 10月 21日

Post: Swift: Optional Protocol Methods Without @objc

Swift: Optional Protocol Methods Without @objc

Published 12:05 May 12, 2016.

Created by @ezra. Categorized in #Programming, and tagged as #iOS.

Source format: Markdown

Table of Content

Objective-C 协议 (protocol) 在 Swift 中是一个特殊的存在, 当你使用 @objc 标记一个协议, LLVM 会生成一系列额外内容:

  • 一个 isa 指针
  • 运行时模块, 例如 __objc_imageinfo__objc_classref

Swift 协议并不支持可选 (optional), 除非我们将协议导出到 Objective-C, 因为这时它们已经变成了 Objective-C 协议。

此外, 这些协议也不能用于扩展 (extension)、枚举 (enum) 和结构体 (struct), 作用十分有限, 如果你在 Linux 环境下使用 Swift, 没有 Objective-C 的运行时环境, 你甚至无法使用这一特性。

进一步了解下 @objc 协议的结构:

                                   ;
                                   ; @protocol _TtP9Protocols8Protocol_ {
                                   ;     -method
                                   ; }
00000001002afcb0                                 dq         0x0                 ; isa, XREF=0x1002abaa0, 0x1002b03c8
00000001002afcb8                                 dq         0x1002782f0         ; name, "_TtP9Protocols8Protocol_"
00000001002afcc0                                 dq         0x0                 ; protocols
00000001002afcc8                                 dq         0x1002afd00         ; instance methods
00000001002afcd0                                 dq         0x0                 ; class methods
00000001002afcd8                                 dq         0x0                 ; optional instanceMethods
00000001002afce0                                 dq         0x0                 ; optional class methods
00000001002afce8                                 dq         0x0                 ; instance properties
00000001002afcf0                                 dd         0x00000050          ; size
00000001002afcf4                                 dd         0x00000001          ; flags
00000001002afcf8                                 db  0x20 ; ' '
00000001002afcf9                                 db  0xfd ; '.'
00000001002afcfa                                 db  0x2a ; '*'
00000001002afcfb                                 db  0x00 ; '.'
00000001002afcfc                                 db  0x01 ; '.'
00000001002afcfd                                 db  0x00 ; '.'
00000001002afcfe                                 db  0x00 ; '.'
00000001002afcff                                 db  0x00 ; '.'
00000001002afd00                                 db  0x18 ; '.'                 ; XREF=0x1002afcc8
00000001002afd01                                 db  0x00 ; '.'
00000001002afd02                                 db  0x00 ; '.'
00000001002afd03                                 db  0x00 ; '.'
00000001002afd04                                 db  0x01 ; '.'
00000001002afd05                                 db  0x00 ; '.'
00000001002afd06                                 db  0x00 ; '.'
00000001002afd07                                 db  0x00 ; '.'
00000001002afd08                                 db  0x0a ; '.'
00000001002afd09                                 db  0xf2 ; '.'
00000001002afd0a                                 db  0x26 ; '&'
00000001002afd0b                                 db  0x00 ; '.'
00000001002afd0c                                 db  0x01 ; '.'
00000001002afd0d                                 db  0x00 ; '.'
00000001002afd0e                                 db  0x00 ; '.'
00000001002afd0f                                 db  0x00 ; '.'
00000001002afd10                                 db  0xe0 ; '.'
00000001002afd11                                 db  0x82 ; '.'
00000001002afd12                                 db  0x27 ; '''
00000001002afd13                                 db  0x00 ; '.'
00000001002afd14                                 db  0x01 ; '.'
00000001002afd15                                 db  0x00 ; '.'
00000001002afd16                                 db  0x00 ; '.'
00000001002afd17                                 db  0x00 ; '.'
00000001002afd18                                 db  0x00 ; '.'
00000001002afd19                                 db  0x00 ; '.'
00000001002afd1a                                 db  0x00 ; '.'
00000001002afd1b                                 db  0x00 ; '.'
00000001002afd1c                                 db  0x00 ; '.'
00000001002afd1d                                 db  0x00 ; '.'
00000001002afd1e                                 db  0x00 ; '.'
00000001002afd1f                                 db  0x00 ; '.'
00000001002afd20                                 db  0xe0 ; '.'
00000001002afd21                                 db  0x82 ; '.'
00000001002afd22                                 db  0x27 ; '''
00000001002afd23                                 db  0x00 ; '.'
00000001002afd24                                 db  0x01 ; '.'
00000001002afd25                                 db  0x00 ; '.'
00000001002afd26                                 db  0x00 ; '.'
00000001002afd27                                 db  0x00 ; '.'
                                   ;
                                   ; Section __objc_selrefs
                                   ;
                                   ; Range 0x1002afd28 - 0x1002b0360 (1592 bytes)
                                   ; File offset 2817320 (1592 bytes)
                                   ; Flags : 0x10000005
                                   ;
00000001002afd28                                 dq         0x10026daa6         ; @selector(hash), "hash", XREF=0x1000008e8, -[NSObject hashValue]+4, __TFE10ObjectiveCCSo8NSObjectg9hashValueSi+4, __TFE10FoundationSSg4hashSi+15, _swift_stdlib_NSStringNFDHashValue+27, _swift_stdlib_NSStringASCIIHashValue+10
00000001002afd30                                 dq         0x10026daab         ; @selector(isEqual:), "isEqual:", XREF=__TTWCSo8NSObjects9Equatable10ObjectiveCZFS0_oi2eefTxx_Sb+16, __TZF10ObjectiveCoi2eeFTCSo8NSObjectS0__Sb+16, _swift_stdlib_NSObject_isEqual+21
00000001002afd38                                 dq         0x10026dab4         ; @selector(hashValue), "hashValue", XREF=__TTWCSo8NSObjects8Hashable10ObjectiveCFS0_g9hashValueSi+7
00000001002afd40                                 dq         0x10026dabe         ; @selector(description), "description", XREF=__TTWC
; assembly code goes by

但如果我们仅仅定义一个普通的协议:

0000000100276b90                                 db         "_TtP9Protocols8Protocol_", 0
                                       ;
                                       ; Section __objc_methname
                                       ;
                                       ; Range 0x100276ba9 - 0x10027830d (5988 bytes)
                                       ; File offset 2583465 (5988 bytes)
                                       ; Flags : 0x00000002
                                       ;
0000000100276ba9                                 db         "hash", 0           ; XREF=0x100000210, 0x1002afcf0
0000000100276bae                                 db         "isEqual:", 0       ; XREF=0x1002afcf8
0000000100276bb7                                 db         "hashValue", 0      ; XREF=0x1002afd00
0000000100276bc1                                 db         "description", 0    ; XREF=0x1002afd08
; assembly code goes by

可见, @objc 协议中的确增添了不少内容。

optional 表示一个方法可能存在也可能不存在, 在 Objective-C 中我们通常再发消息之前进行检查, Swift 中我们使用可选类型 (?) 直接调用即可。那么, 我们其实也可以通过给协议增加扩展来实现 Swift 版的 optional

protocol Protocol: class {
    func requiredMethodOne()
    func requiredMethodTwo()
}

extension Protocol {
    func optionalMethodOne() {}
    func optionalMethodTwo() {}
}

这样, 即使你调用没有实现的 optionalMethodOne() 也不会发生任何事不可预测的事情。

Pinned Message
HOTODOGO
The Founder and CEO of Infeca Technology.
Developer, Designer, Blogger.
Big fan of Apple, Love of colour.
Feel free to contact me.
反曲点科技创始人和首席执行官。
开发、设计与写作皆为所长。
热爱苹果、钟情色彩。
随时恭候 垂询