Swift中通过OC的运行时给系统的类添加闭包属性 | 使用类扩展(extension)
Swift中使用typealias定义一个闭包(无返回值,有一个数组类型参数)
typealias XYRearrangeNewDataBlock = (_ newData: [Any]) -> Void
OC 使用typedef定义(无返回值,有一个数组类型参数)
typedef void(^XYRollNewDataBlock)(NSArray * newData);
在Swift类扩展中声明闭包属性
由于Swift扩展不接受这些存储的属性,所以可以通过关联属性存储属性,比如下面给UICollectionView的扩展增加两个闭包属性
(错误写法,如果非闭包属性可以这么,由于写习惯了OC,由于Swift不熟练,下面这个错误我搞了3个小时才解决,记录是为了自己不再次出错)
public extension UICollectionView {
typealias XYRearrangeNewDataBlock = () -> Void
typealias XYRearrangeOriginaDataBlock = () -> [String]
// MARK:- 关联属性的key
struct xy_associatedKeys {
static var originalDataBlockKey = "originalDataBlockKey"
static var newDataBlockKey = "newDataBlockKey"
}
// 声明闭包属性,并通过set函数与当前类产生关联,get函数获取关联
var originalDataBlock : XYRearrangeNewDataBlock? {
get {
if let originalDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.originalDataBlockKey) as? XYRearrangeNewDataBlock {
return originalDataBlock
}
return nil
}
set(newValue) {
❌ 总是在set函数中报编译错误
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)
}
}
// 将闭包属性作为便利构造函数的参数
convenience init(collectionVewFlowLayout : UICollectionViewFlowLayout, originalDataBlock: XYRearrangeOriginaDataBlock, newDataBlock: XYRearrangeNewDataBlock) {
self.init()
}
}
错误信息:
Command failed due to signal: Segmentation fault: 11
在Swift的类扩展中关联闭包block属性时,总是报编译失败错误,最终把错误定义在objc_setAssociatedObject关联闭包属性时
解决方法:
要先定义一个类属性作为闭包容器,专门存放闭包的属性,以下是正确的在extension中给类添加闭包属性的方法
注意: 如果便利构造函数中有闭包参数,外界调用这个方法时,需要自己把闭包参数改为大括号{}
以下是正确添加关联闭包属性的方法
import UIKit
public extension UICollectionView {
typealias XYRearrangeNewDataBlock = () -> Void
typealias XYRearrangeOriginaDataBlock = () -> [String]
// MARK:- 关联属性的key
private struct xy_associatedKeys {
static var originalDataBlockKey = "xy_originalDataBlockKey"
static var newDataBlockKey = "xy_newDataBlockKey"
}
// 定义一个类属性作为闭包的容器,专门存放闭包的属性
private class BlockContainer: NSObject, NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
return self
}
var rearrangeNewDataBlock: XYRearrangeNewDataBlock?
var rearrangeOriginaDataBlock: XYRearrangeOriginaDataBlock?
}
// 定义个一个计算属性,通过OC的运行时获取关联对象和设置关联对象
private var newDataBlock: BlockContainer? {
get {
if let newDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.newDataBlockKey) as? BlockContainer {
return newDataBlock
}
return nil
}
// 如果计算属性的setter没有定义表示新值的参数名,则可以用默认值newValue
set(newValue) {
objc_setAssociatedObject(self, xy_associatedKeys.newDataBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
// 通过外界调用便利构造函数时,给闭包属性赋值
convenience init(collectionVewFlowLayout : UICollectionViewFlowLayout, originalDataBlock: @escaping XYRearrangeOriginaDataBlock, newDataBlock: @escaping XYRearrangeNewDataBlock) {
self.init()
// 创建blockContainer,将外界传来的闭包赋值给类属性中的闭包变量
let blockContainer: BlockContainer = BlockContainer()
blockContainer.rearrangeNewDataBlock = newDataBlock
blockContainer.rearrangeOriginaDataBlock = originalDataBlock
self.newDataBlock = blockContainer
}
}
以下是与今天遇到的错误有关的Swift基础知识点
- 计算属性
1.Swift中的计算属性不直接存储值
跟存储属性不同,没有任何的”后端存储与之对应”
2.计算属性用于计算, 提供一个getter和一个可选的setter来间接获取、设置其他属性和变量的值。如果计算属性的setter没有定义表示新值的参数名,则可以用默认值
3.枚举不可以有存储属性, 但是允许有计算属性
- 结构体和类常量与存储属性的关系
结构体和枚举是值类型 ,因此不能修改结构体常量中的属性
不能修改结构体/枚举常量对象中的值, 因为他指向的对象是一个常量
类是引用类型 ,可以修改类常量中属性的值, 因为他指向的对象不是一个常量
struct Person2 { // 结构体
var name: String
var age: Int
}
let p2: Person2 = Person2(name: "cdh", age: 20)
//因为结构体是值类型, 所以不能修改结构体常量中的属性
//不能修改结构体/枚举常量对象中的值, 因为他指向的对象是一个常量
//以下写法错误
//p2.name = "CDH" //不能修改结构体常量对象的值
//以下写法错误
//p2 = Person2(name: "CDH", age: 50)
class Person3 { // 类属性
var name: String = "cdh"
var age: Int = 20
}
let p3:Person3 = Person3()
//可以修改类常量中属性的值, 因为他指向的对象不是一个常量
p3.name = "CDH"
//不可以修改类常量的指向
//以下写法是错误的
//p3 = Person4()
- 类属性
在结构体和枚举中用static
在类中使用class, 并且类中不允许将存储属性设置为类属性
struct Person4 {
// 普通的属性是每个对象一份
var name: String = "cdh"
// 类属性是所有相同类对象共用一份
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)")
//输出结果: gender = man
var p5 = Person4()
//类属性是所有对象共用一份
print("gender = \(Person4.gender)")
p5.show()
//输出结果:
//gender = man
//gender = man name = cdh
//可以将计算属性设置为类属性
print("age = \(Person4.age)")
//输出结果:age = 20
class Person5 {
// 普通的属性是每个对象一份
var name: String = "cdh"
// 类中不允许将存储属性定义为类属性
// 下面为错误写法
// class var gender:String = "man"
// 类中只能将计算属性定义为类属性
class var age:Int{
return 20
}
func show()
{
print("age = \(Person5.age)")
}
}
var p6 = Person5()
print("age = \(Person5.age)")
p6.show()
//输出结果:
//age = 20
//age = 20