類簇(Class Clusters)用通俗一點講就是一個public
的抽象類加上一些private
的私有類構成的,它是對一些實現細節進行隱藏,而對外公開的行為進行統一的一種設計。相信大家在平常工作中多少有注意到一些蛛絲馬跡。例如我們常用的NSNumber
, NSArray
, NSDictionary
以及NSString
等,而這些都是總所周知。
然而就UIButton
是不是類簇,本王就糾結了。To be or not to be, that’s a question. 這時候就應該裝逼了,搬出莎士比亞這句話。 順帶就帶著這個來說說類簇的問題。關於為什麼糾結呢?因為很多地方包括書籍都提到UIButton
是類簇,而我再Stack Overflow卻找到這樣一段話:
UIButton is not a class cluster at all. A class cluster is represented by a public abstract class, that means no instance variables, with a bunch of private concrete subclasses that provide the implementation of the abstract methods of the abstract class. UIButton on the other hand is a concrete class, none of its methods is abstract, and it has instance variables to store the value you pass through its arguments. The only problematic part is that +buttonWithType can instantiate subclasses instead of UIButton directly, thus it can be seen as a factory method, not a class-cluster…
然後我就懵逼了,根據類簇大體的概念我們知道至少說如果UIButton
是一個抽象類的話,那麼應該還存在一些private
的私有類來實現具體的細節。
那麼我們的任務就是就是找到這些私有類,之前看了@我就叫Sunny怎麼了 的一篇文章從NSArray看類簇後發現至少UIButton
中沒辦法用這種方法,於是就想說用LLDB斷點一下,用了下面這條命令:
1 |
breakpoint set -F '+[UIButton buttonWithType:]' |
得到了這樣一段彙編:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
UIKit`+[UIButton buttonWithType:]: -> 0x1063251e5 : pushq %rbp ..... // 省略 0x106325297 : movq 0x972c02(%rip), %rdi ; (void *)0x0000000106cbabf8: UIButton 0x10632529e : movq 0x93cf6b(%rip), %rsi ; "alloc" 0x1063252a5 : movq 0x9c0f34(%rip), %rbx ; (void *)0x0000000105650800: objc_msgSend 0x1063252ac : callq *%rbx 0x1063252ae : movq 0x93ce2b(%rip), %rsi ; "initWithFrame:" ..... // 省略 0x10632531c : movq 0x97427d(%rip), %rdi ; (void *)0x0000000106cbad10: UIPopoverButton 0x106325323 : movq 0x93cee6(%rip), %rsi ; "alloc" 0x10632532a : movq 0x9c0eaf(%rip), %rbx ; (void *)0x0000000105650800: objc_msgSend 0x106325331 : callq *%rbx 0x106325333 : movq 0x9557a6(%rip), %rsi ; "initWithFrame:buttonType:" 0x10632533a : movq -0x38(%rbp), %rcx ..... // 省略 0x1063253c5 : movq 0x9741c4(%rip), %rdi ; (void *)0x0000000106cbac48: UIRoundedRectButton 0x1063253cc : jmp 0x106325553 ; 0x1063253d1 : movb %r14b, -0x81(%rbp) 0x1063253d8 : movq 0x972ac1(%rip), %rdi ; (void *)0x0000000106cbabf8: UIButton 0x1063253df : jmp 0x106325553 ; 0x1063253e4 : movb %r14b, -0x81(%rbp) 0x1063253eb : movq %rbx, -0x80(%rbp) 0x1063253ef : movq 0x973262(%rip), %rdi ; (void *)0x0000000106cb45a0: UINavigationButton 0x1063253f6 : movq 0x93ce13(%rip), %rsi ; "alloc" 0x1063253fd : movq 0x9c0ddc(%rip), %rbx ; (void *)0x0000000105650800: objc_msgSend 0x106325404 : callq *%rbx 0x106325406 : movq 0x9556cb(%rip), %rsi ; "initWithTitle:style:" 0x10632540d : xorl %edx, %edx 0x10632540f : xorl %ecx, %ecx 0x106325411 : jmp 0x106325475 ; 0x106325413 : movb %r14b, -0x81(%rbp) 0x10632541a : movq %rbx, -0x80(%rbp) 0x10632541e : movq 0x973233(%rip), %rdi ; (void *)0x0000000106cb45a0: UINavigationButton 0x106325425 : movq 0x93cde4(%rip), %rsi ; "alloc" 0x10632542c : movq 0x9c0dad(%rip), %rbx ; (void *)0x0000000105650800: objc_msgSend 0x106325433 : callq *%rbx 0x106325435 : movq 0x95569c(%rip), %rsi ; "initWithTitle:style:" 0x10632543c : xorl %edx, %edx 0x10632543e : movl $0x1, %ecx 0x106325443 : jmp 0x106325475 ; 0x106325445 : movb %r14b, -0x81(%rbp) 0x10632544c : movq %rbx, -0x80(%rbp) 0x106325450 : movq 0x973201(%rip), %rdi ; (void *)0x0000000106cb45a0: UINavigationButton 0x106325457 : movq 0x93cdb2(%rip), %rsi ; "alloc" 0x10632545e : movq 0x9c0d7b(%rip), %rbx ; (void *)0x0000000105650800: objc_msgSend 0x106325465 : callq *%rbx 0x106325467 : movq 0x95566a(%rip), %rsi ; "initWithTitle:style:" ..... // 省略 0x10632549e : movq 0x9740f3(%rip), %rdi ; (void *)0x0000000106cbacc0: UITexturedButton 0x1063254a5 : jmp 0x106325553 ; 0x1063254aa : movb %r14b, -0x81(%rbp) 0x1063254b1 : movq %rbx, -0x80(%rbp) 0x1063254b5 : movq 0x9740d4(%rip), %rdi ; (void *)0x0000000106cbac48: UIRoundedRectButton 0x1063254bc : movq 0x93cd4d(%rip), %rsi ; "alloc" 0x1063254c3 : movq 0x9c0d16(%rip), %r14 ; (void *)0x0000000105650800: objc_msgSend ..... // 省略 0x106325538 : movq 0x974069(%rip), %rdi ; (void *)0x0000000106cbad60: _UIPlacardButton 0x10632553f : jmp 0x106325553 ; 0x106325541 : movb %r14b, -0x81(%rbp) 0x106325548 : movq %rbx, -0x80(%rbp) 0x10632554c : movq 0x97405d(%rip), %rdi ; (void *)0x0000000106cbadb0: _UIShortPlacardButton 0x106325553 : movq 0x93ccb6(%rip), %rsi ; "alloc" 0x10632555a : movq 0x9c0c7f(%rip), %rbx ; (void *)0x0000000105650800: objc_msgSend 0x106325561 : callq *%rbx 0x106325563 : movq 0x93cb76(%rip), %rsi ; "initWithFrame:" ..... // 省略 |
看到這裡小夥伴不要害怕,吃口粑粑冷靜一下。我不是要大家看每一句話是什麼意思,大家可以注意一下每一行最後又Button
關鍵字的地方,會發現一堆我們平時沒見過的一些Button
,如UIPopoverButton
, UIRoundedRectButton
, UINavigationButton
, UITexturedButton
, _UIPlacardButton
以及_UIShortPlacardButton
等。至少我們發現了我們所說的private
的私有類,那麼我們就可以知道其實這裡的UIButton
是一個類簇。
那麼我們也用官網的圖來說說類簇的用處:
可以從上圖看到雖然官方的實現細節根據不同的型別有不同的實現,但是我們所需要記得以及互動的介面都可以通過NSNumer
來進行互動。而如果我們自己在設計類簇的時候也可能通過這種方式來進行隱藏一些細節的實現。
但是在設計自己的類簇的過程中需要注意一下幾點:
- 首先要定義好抽象基類
- 其次需要指明子類需要重寫的方法
- 最後提供一個比較好的文件說明方面其他人讀寫
最後就到這吧,小夥伴各回各家,各找各媽吧~
PS:具體程式碼可以從Github上獲取。
如有問題或糾正, 可以聯絡@叫什麼都不如叫Pluto-Y或在Github
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式