在swift項目中,一般是用Extension的方式來組織代碼,將實現(xiàn)的各種協(xié)議等分別寫在不同的擴展中。
class ZYViewController: UIViewController {}
extension ZYViewController: UITableViewDelegate{}
extension ZYViewController: UINavigationControllerDelegate{}
但是Extension只能直接添加計算屬性,不能直接添加存儲屬性,這一點和OC的分類很像。不過,同樣我們可以使用關(guān)聯(lián)屬性來解決這個問題。
Extension中添加關(guān)聯(lián)屬性的一般寫法
在Extension中定義一個私有的AssociatedKeys結(jié)構(gòu)體,然后為其添加靜態(tài)屬性testAssociated,使用&AssociatedKeys.testAssociated對關(guān)聯(lián)屬性進行存取。
extension UIViewController {
private struct AssociatedKeys {
static var testAssociated: String = "testAssociatedKey"http://也可以是其他類型
}
var testAssociated: String {
get{
guard let t = objc_getAssociatedObject(self, &AssociatedKeys.testAssociated) as? String else {
return ""
}
return t
}
set{
objc_setAssociatedObject(self, &AssociatedKeys.testAssociated, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
OC添加關(guān)聯(lián)屬性
一開始在OC中添加關(guān)聯(lián)屬性時,也是單獨定義一個Key。后來發(fā)現(xiàn)了一種取巧的辦法,就是直接使用_cmd。
- (NSString *)testAssociated
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setTestAssociated:(NSString *)testAssociated
{
objc_setAssociatedObject(self, @selector(testAssociated), testAssociated, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
根據(jù)OC的寫法使用#function添加關(guān)聯(lián)屬性(有bug)
#function是swift2.2中增加的,用于獲取所在方法的name。其作用和OC的_cmd是一樣的。于是修改關(guān)聯(lián)屬性寫法。
var testAssociated:String {
get{
guard let t = objc_getAssociatedObject(self, #function) as? String else {
return ""http://默認值
}
return t
}set{
objc_setAssociatedObject(self, #function, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
在使用時發(fā)現(xiàn)get只是偶爾能獲取到正確的值,經(jīng)常都是默認值。
可見直接使用#function是行不通的。查看類型發(fā)現(xiàn)#functon是String類型,而_cmd在Objective-C的方法中表示當前方法的selector。直接使用不行,就沒有辦法了?
使用#function添加關(guān)聯(lián)屬性的正確姿勢
既然#function不能直接使用,能不能將其轉(zhuǎn)化為一個穩(wěn)定的值?查看objc_getAssociatedObject的key,發(fā)現(xiàn)其是一個UnsafeRawPointer類型。于是將#function轉(zhuǎn)換為Data再轉(zhuǎn)為UnsafeRawPointer。
let key = #function.data(using: .utf8)?.withUnsafeBytes({ (uint8Ptr: UnsafePointer<UInt8>) -> UnsafeRawPointer in
return UnsafeRawPointer(uint8Ptr)
})
但是最終發(fā)現(xiàn)這個key其實也是一個隨機值。于是修改最終代碼如下。使用#function作為key來緩存轉(zhuǎn)換后的值,每次轉(zhuǎn)換前先從keysDic中查找。
struct AssociationKey {
static var keysDic:[String: UnsafeRawPointer] = [:]
static func from(_ str:String) -> UnsafeRawPointer{
var key = keysDic[str]
if key == nil {
key = str.data(using: .utf8)?.withUnsafeBytes({ (uint8Ptr: UnsafeRawBufferPointer) -> UnsafeRawPointer in
return uint8Ptr.load(as: UnsafeRawPointer.self)
})
keysDic[str] = key
}
return key!
}
}
最后我們可以修改關(guān)聯(lián)屬性寫法如下
var testAssociated:String {
get{
guard let t = objc_getAssociatedObject(self, AssociationKey.from(#function)) as? String else {
return ""
}
return t
}set{
objc_setAssociatedObject(self, AssociationKey.from(#function), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}