在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)
}
}