Swift中Selector方法的訪問許可權控制問題
今天用Swift寫了個檢視,在檢視上加個手勢,如下所示:
1 2 |
panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "beginDragged:") addGestureRecognizer(panGestureRecognizer) |
執行了下程式,然後崩潰了。崩潰日誌如下:
1 |
[**.SwipeCardView beginDragged:]: unrecognized selector sent to instance 0x125e5bc10 |
而我已經在SwipeCardView類中定義了beginDragged:方法,如下所示:
1 2 3 |
private func beginDragged(gestureRecognizer: UIPanGestureRecognizer) { // .... } |
由於我並不想將beginDragged:方法暴露出去,所以將其定義為一個private方法。方法的定義一切正常,手勢的Selector方法也設定正常,卻報了未找到方法的異常。那問題可能就應該在訪問許可權問題上了。
我們知道Selector是Objective-C的產物,它用於在執行時作為一個鍵值去找到對應方法的實現。一個Objective-C的方法是由objc_method結束體定義的,其宣告如下:
1 2 3 4 5 6 |
struct objc_method { SEL method_name OBJC2_UNAVAILABLE; // 方法名 char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; // 方法實現 } |
這就要求selector引用的方法必須對ObjC執行時是可見的。而Swift是靜態語言,雖然繼承自NSObject的類預設對ObjC執行時是可見的,但如果方法是由private關鍵字修飾的,則方法預設情況下對ObjC執行時並不是可見的,所以就導致了以上的異常:執行時並沒找到SwipeCardView類的beginDragged:方法。
所以,我們必須將private修飾的方法暴露給執行時。正確的做法是在 private 前面加上 @objc 關鍵字,這樣就OK了。
1 2 3 |
@objc private func beginDragged(gestureRecognizer: UIPanGestureRecognizer) { // .... } |
另外需要注意的是,如果我們的類是純Swift類,而不是繼承自NSObject,則不管方法是private還是internal或public,如果要用在Selector中,都需要加上@objc修飾符。
參考
零碎
Swift中列舉項設定相同的值
在Objective-C及C語言中,在列舉中我們可以設定兩個列舉項的值相等,如下所示:
1 2 3 4 5 6 |
typedef NS_ENUM(NSUInteger, Type) { TypeIn = 0, TypeOut = 1, TypeInOut = 2, TypeDefault = TypeIn }; |
在上例中,我們讓列舉項TypeDefault的值等於TypeIn。
而在Swift中,要求列舉項的rawValue是唯一的,如果像下面這樣寫,則編譯器會報錯:
1 2 3 4 5 6 |
enum Type: UInt { case In = 0 case Out = 1 case InOut = 2 case Default = 0 // Error: Raw value for enum case is not unique } |
那如果我們希望上面列舉中Default的值與In的值一樣,那應該怎麼做呢?這時候就需要充分利用Swift中enum的特性了。我們知道,Swift中的enum與結構體、類一樣,可以為其定義屬性和方法,所以我們可以如下處理:
1 2 3 4 5 6 7 8 9 10 11 |
enum Type: UInt { case In = 0 case Out = 1 case InOut = 2 static var Default: Type { get { return .In } } } |
我們將Default定義為Type的一個靜態只讀屬性,這個屬性與列舉的其它列舉項的呼叫方式是一樣的,可以如下呼叫:
1 |
let type: Type = .Default |
參考
Swift中如何實現IBOutletCollection
在使用IB做介面開發時,我們經常需要將介面上的元素連線到我們的程式碼中。IBOutlet和IBAction就是專門用來做這事的兩個關鍵字。另外在Objective-C還提供了一個偽關鍵字IBOutletCollection,它的實際作用是將介面上的一組相同的控制元件連線到一個陣列中。具體可以參考iOS知識小集 第一期(2015.05.10)中的IBOutletCollection一節。
在Swift中,同樣提供了@IBOutlet和@IBAction實現Objective-C中對應的功能,不過卻沒提供@IBOutletCollection來將一組相同控制元件連線到一個陣列中。那如果我們想實現類似的功能,需要怎麼處理呢?
實際上,我們在IB中選中一組相同的控制元件,然後將其連到到程式碼中時,會生成一個IBOutlet修飾的控制元件陣列,類似於如下程式碼:
1 |
@IBOutlet var numberButtons: [UIButton]! |
這就是Swift中類IBOutletCollection的處理。如果需要往陣列中新增新建的對應的控制元件,則只需要在程式碼前面的小圓點與UI上的控制元件做個連線就OK了。而如果要想將控制元件從陣列中移除,則只需要將對應的連線關係移除就可以了。
參考