Swift中通過OC的運(yùn)行時(shí)給系統(tǒng)的類添加閉包屬性 | 使用類擴(kuò)展(extension)
Swift中使用typealias定義一個(gè)閉包(無(wú)返回值穗椅,有一個(gè)數(shù)組類型參數(shù))
typealias XYRearrangeNewDataBlock = (_ newData: [Any]) -> Void
OC 使用typedef定義(無(wú)返回值,有一個(gè)數(shù)組類型參數(shù))
typedef void(^XYRollNewDataBlock)(NSArray * newData);
在Swift類擴(kuò)展中聲明閉包屬性
由于Swift擴(kuò)展不接受這些存儲(chǔ)的屬性垂谢,所以可以通過關(guān)聯(lián)屬性存儲(chǔ)屬性,比如下面給UICollectionView的擴(kuò)展增加兩個(gè)閉包屬性
(錯(cuò)誤寫法风钻,如果非閉包屬性可以這么筐高,由于寫習(xí)慣了OC赃阀,由于Swift不熟練,下面這個(gè)錯(cuò)誤我搞了3個(gè)小時(shí)才解決趾痘,記錄是為了自己不再次出錯(cuò))
public extension UICollectionView {
typealias XYRearrangeNewDataBlock = () -> Void
typealias XYRearrangeOriginaDataBlock = () -> [String]
// MARK:- 關(guān)聯(lián)屬性的key
struct xy_associatedKeys {
static var originalDataBlockKey = "originalDataBlockKey"
static var newDataBlockKey = "newDataBlockKey"
}
// 聲明閉包屬性慢哈,并通過set函數(shù)與當(dāng)前類產(chǎn)生關(guān)聯(lián),get函數(shù)獲取關(guān)聯(lián)
var originalDataBlock : XYRearrangeNewDataBlock? {
get {
if let originalDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.originalDataBlockKey) as? XYRearrangeNewDataBlock {
return originalDataBlock
}
return nil
}
set(newValue) {
? 總是在set函數(shù)中報(bào)編譯錯(cuò)誤
objc_setAssociatedObject(self, &xy_associatedKeys.originalDataBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
var newDataBlock: XYRearrangeNewDataBlock? {
get {
if let newDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.newDataBlockKey) as? XYRearrangeNewDataBlock {
return newDataBlock
}
return nil
}
set(newValue) {
objc_setAssociatedObject(self, xy_associatedKeys.newDataBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
// 將閉包屬性作為便利構(gòu)造函數(shù)的參數(shù)
convenience init(collectionVewFlowLayout : UICollectionViewFlowLayout, originalDataBlock: XYRearrangeOriginaDataBlock, newDataBlock: XYRearrangeNewDataBlock) {
self.init()
}
}
錯(cuò)誤信息:
Command failed due to signal: Segmentation fault: 11
在Swift的類擴(kuò)展中關(guān)聯(lián)閉包block屬性時(shí)永票,總是報(bào)編譯失敗錯(cuò)誤卵贱,最終把錯(cuò)誤定義在objc_setAssociatedObject關(guān)聯(lián)閉包屬性時(shí)
解決方法:
要先定義一個(gè)類屬性作為閉包容器,專門存放閉包的屬性侣集,以下是正確的在extension中給類添加閉包屬性的方法
注意: 如果便利構(gòu)造函數(shù)中有閉包參數(shù)艰赞,外界調(diào)用這個(gè)方法時(shí),需要自己把閉包參數(shù)改為大括號(hào){}
以下是正確添加關(guān)聯(lián)閉包屬性的方法
import UIKit
public extension UICollectionView {
typealias XYRearrangeNewDataBlock = () -> Void
typealias XYRearrangeOriginaDataBlock = () -> [String]
// MARK:- 關(guān)聯(lián)屬性的key
private struct xy_associatedKeys {
static var originalDataBlockKey = "xy_originalDataBlockKey"
static var newDataBlockKey = "xy_newDataBlockKey"
}
// 定義一個(gè)類屬性作為閉包的容器肚吏,專門存放閉包的屬性
private class BlockContainer: NSObject, NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
return self
}
var rearrangeNewDataBlock: XYRearrangeNewDataBlock?
var rearrangeOriginaDataBlock: XYRearrangeOriginaDataBlock?
}
// 定義個(gè)一個(gè)計(jì)算屬性方妖,通過OC的運(yùn)行時(shí)獲取關(guān)聯(lián)對(duì)象和設(shè)置關(guān)聯(lián)對(duì)象
private var newDataBlock: BlockContainer? {
get {
if let newDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.newDataBlockKey) as? BlockContainer {
return newDataBlock
}
return nil
}
// 如果計(jì)算屬性的setter沒有定義表示新值的參數(shù)名,則可以用默認(rèn)值newValue
set(newValue) {
objc_setAssociatedObject(self, xy_associatedKeys.newDataBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
// 通過外界調(diào)用便利構(gòu)造函數(shù)時(shí)罚攀,給閉包屬性賦值
convenience init(collectionVewFlowLayout : UICollectionViewFlowLayout, originalDataBlock: @escaping XYRearrangeOriginaDataBlock, newDataBlock: @escaping XYRearrangeNewDataBlock) {
self.init()
// 創(chuàng)建blockContainer,將外界傳來的閉包賦值給類屬性中的閉包變量
let blockContainer: BlockContainer = BlockContainer()
blockContainer.rearrangeNewDataBlock = newDataBlock
blockContainer.rearrangeOriginaDataBlock = originalDataBlock
self.newDataBlock = blockContainer
}
}
以下是與今天遇到的錯(cuò)誤有關(guān)的Swift基礎(chǔ)知識(shí)點(diǎn)
- 計(jì)算屬性
1.Swift中的計(jì)算屬性不直接存儲(chǔ)值
跟存儲(chǔ)屬性不同,沒有任何的”后端存儲(chǔ)與之對(duì)應(yīng)”
2.計(jì)算屬性用于計(jì)算, 提供一個(gè)getter和一個(gè)可選的setter來間接獲取党觅、設(shè)置其他屬性和變量的值雌澄。如果計(jì)算屬性的setter沒有定義表示新值的參數(shù)名,則可以用默認(rèn)值
3.枚舉不可以有存儲(chǔ)屬性, 但是允許有計(jì)算屬性
- 結(jié)構(gòu)體和類常量與存儲(chǔ)屬性的關(guān)系
結(jié)構(gòu)體和枚舉是值類型 ,因此不能修改結(jié)構(gòu)體常量中的屬性
不能修改結(jié)構(gòu)體/枚舉常量對(duì)象中的值, 因?yàn)樗赶虻膶?duì)象是一個(gè)常量
類是引用類型 ,可以修改類常量中屬性的值, 因?yàn)樗赶虻膶?duì)象不是一個(gè)常量
struct Person2 { // 結(jié)構(gòu)體
var name: String
var age: Int
}
let p2: Person2 = Person2(name: "cdh", age: 20)
//因?yàn)榻Y(jié)構(gòu)體是值類型, 所以不能修改結(jié)構(gòu)體常量中的屬性
//不能修改結(jié)構(gòu)體/枚舉常量對(duì)象中的值, 因?yàn)樗赶虻膶?duì)象是一個(gè)常量
//以下寫法錯(cuò)誤
//p2.name = "CDH" //不能修改結(jié)構(gòu)體常量對(duì)象的值
//以下寫法錯(cuò)誤
//p2 = Person2(name: "CDH", age: 50)
class Person3 { // 類屬性
var name: String = "cdh"
var age: Int = 20
}
let p3:Person3 = Person3()
//可以修改類常量中屬性的值, 因?yàn)樗赶虻膶?duì)象不是一個(gè)常量
p3.name = "CDH"
//不可以修改類常量的指向
//以下寫法是錯(cuò)誤的
//p3 = Person4()
- 類屬性
在結(jié)構(gòu)體和枚舉中用static
在類中使用class, 并且類中不允許將存儲(chǔ)屬性設(shè)置為類屬性
struct Person4 {
// 普通的屬性是每個(gè)對(duì)象一份
var name: String = "cdh"
// 類屬性是所有相同類對(duì)象共用一份
static var gender:String = "man"
static var age:Int{
return 20
}
func show()
{
print("gender = \(Person4.gender) name = \(name)")
}
}
var p4 = Person4()
print("gender = \(Person4.gender)")
//輸出結(jié)果: gender = man
var p5 = Person4()
//類屬性是所有對(duì)象共用一份
print("gender = \(Person4.gender)")
p5.show()
//輸出結(jié)果:
//gender = man
//gender = man name = cdh
//可以將計(jì)算屬性設(shè)置為類屬性
print("age = \(Person4.age)")
//輸出結(jié)果:age = 20
class Person5 {
// 普通的屬性是每個(gè)對(duì)象一份
var name: String = "cdh"
// 類中不允許將存儲(chǔ)屬性定義為類屬性
// 下面為錯(cuò)誤寫法
// class var gender:String = "man"
// 類中只能將計(jì)算屬性定義為類屬性
class var age:Int{
return 20
}
func show()
{
print("age = \(Person5.age)")
}
}
var p6 = Person5()
print("age = \(Person5.age)")
p6.show()
//輸出結(jié)果:
//age = 20
//age = 20