封装 UserDefaults
,用属性的方式存取数据
-
告别字符串硬编码,
-
统一形式,不再有各种类型存取方法
double(forKey: _ )
integer(forKey: _)
主要有参考 woshiccm/RCUserDefaults
调用
print(UserDefaults.std.name)
UserDefaults.std.name = "two"
print(UserDefaults.std.name)
配置
添加属性
extension RCUserDefaults{
@NSManaged var name: String
}
原理
- 一般给类添加属性,通过
objc_getAssociatedObject
添加的属性,放在内存中
- 这里添加的属性,通过持久化的方式,放在磁盘中,
这时候直接添加存取方法,就好。
类似 Core Data , 需引入 @NSManaged
流程
-
为了方便扩展,添加的属性放在扩展中
-
通过
class_copyPropertyList
, 拿到添加的属性 -
给每一个属性,实现对应类型的存取方法,就好了
添加方法,使用 class_addMethod
添加的方法,自然是 double(forKey: _)
与 set(_ , forKey: _ )
…
- 为了知道添加属性,对应的类型。使用
property_copyAttributeList
实现
添加的属性,放在扩展中
做事情的是这个类 RCUserDefaults
,
为了简化写法,里面有一个属性是 UserDefaults.standard
UserDefaults.standard
负责,实际的存取
class RCUserDefaults: NSObject{
private let userDefaults = UserDefaults.standard
}
添加扩展属性
extension RCUserDefaults{
@NSManaged var name: String
}
如果没有 @NSManaged
, 报错
Extensions must not contain stored properties
扩展,不能包含属性
与 Core Data 类似,添加 @NSManaged
,就好了
the @NSManaged attribute informs the Swift compiler that the storage and implementation of a property will be provided at runtime.
添加 @NSManaged
,编译时不再检查,运行时保证存取即可
拿到添加的属性
常规的运行时套路, class_copyPropertyList
extension NSObject {
static var properties: [Property] {
var count: UInt32 = 0
guard let propertyList = class_copyPropertyList(self, &count) else { return [] }
var properties = [Property]()
let cnt = Int(count)
for i in 0..<cnt{
properties.append(Property(x: propertyList[i]))
}
free(propertyList)
return properties
}
}
知道,添加属性对应的类型
才能在运行时,添加对应的 IMP,
是
double(forKey: _ )
还是
integer(forKey: _)
调用入口
properties.append(Property(x: propertyList[i]))
代码:
拿到 objc_property_t
, 取各种属性 property_copyAttributeList
struct Property {
let name: String
private(set) var typeEncoding = ObjCTypeEncoding.unknown("?")
private(set) var type: NSObject.Type?
init(x property: objc_property_t) {
// 拿到,我们赋予的属性名
name = String(cString: property_getName(property))
var count: UInt32 = 0
let attributeList = property_copyAttributeList(property, &count)!
for i in 0..<Int(count) {
let attribute = attributeList[i]
let nick = String(cString: attribute.name)
let value = String(cString: attribute.value)
switch nick {
case "T":
var value = value
if value.hasPrefix("@\"") && value.hasSuffix("\"") { // id
value = value
.replacingOccurrences(of: "@\"", with: "")
.replacingOccurrences(of: "\"", with: "")
}
else if value.hasPrefix("r") { // const
value = value.replacingOccurrences(of: "r", with: "")
}
// 判断是类,还是值类型
if value.classExists{
// string
// stringOptional
// data
typeEncoding = ObjCTypeEncoding(e: "@")
type = value.getClass
}
else {
// bool
// double
typeEncoding = ObjCTypeEncoding(e: value)
}
// ...
default: break
}
}
// ...
}
}
最后一步,给每一个属性,实现对应类型的存取方法
又来到了这个类 RCUserDefaults
class RCUserDefaults: NSObject{
// 单例
fileprivate static let standard = RCUserDefaults()
// 记录信息
// getter 方法签名字符串: 属性
// setter 方法签名字符串: 属性
private static var mapping = [String: Property]()
// 初始化方法,比较别致,
// 参数,就是走一个形式
// 为了这个单例, static let standard = RCUserDefaults()
// 调用运行时添加 getter / setter 方法, exchangeAccessMethods()
public init(placeHolder nan: Bool? = nil){
super.init()
exchangeAccessMethods()
}
}
运行时添加 getter / setter
方法
private func exchangeAccessMethods(){
let properties = RCUserDefaults.properties
// 处理每一个属性
for property in properties{
let getterKey = property.name
let setterKey = objCDefaultSetterName(for: property.name)
// 记录信息
// getter 方法签名字符串: 属性
RCUserDefaults.mapping[getterKey] = property
// 记录信息
// setter 方法签名字符串: 属性
RCUserDefaults.mapping[setterKey] = property
let getterSel : Selector = NSSelectorFromString(getterKey)
let setterSel : Selector = NSSelectorFromString(setterKey)
var getterImp: IMP!
var setterImp: IMP!
switch property.typeEncoding {
case .int, .longLong, .uInt8:
getterImp = unsafeBitCast(RCUserDefaults.longGetter, to: IMP.self)
setterImp = unsafeBitCast(RCUserDefaults.longSetter, to: IMP.self)
// ...
// 处理其他类型
}
let setterTypes = "v@:\(property.typeEncoding)"
let getterTypes = "\(property.typeEncoding)@:"
setterTypes.withCString { typesCString in
_ = class_addMethod(classForCoder, setterSel, setterImp, typesCString)
}
getterTypes.withCString { typesCString in
_ = class_addMethod(classForCoder, getterSel, getterImp, typesCString)
}
}
}
最后 IMP
其中一种类型的 getter
和 setter
,
下面是 long
,长整型
private static let longGetter: @convention(c) (RCUserDefaults, Selector) -> CLong = { _userDefault, _cmd in
// 从 getter 方法,拿到我们赋予的属性名
let key = defaultKey(for: _cmd)
// 又看到了熟悉的取方法
return _userDefault.userDefaults.integer(forKey: key)
}
private static let longSetter: @convention(c) (RCUserDefaults, Selector, CLong) -> Void = { _userDefault, _cmd, value in
// 从 setter 方法,拿到我们赋予的属性名
let key = defaultKey(for: _cmd)
// 又看到了熟悉的存方法
_userDefault.userDefaults.set(value, forKey: key)
}
回到开头,添加命名空间
给 UserDefaults
添加计算属性,
返回的是 RCUserDefaults
的单例
extension UserDefaults{
static var std: RCUserDefaults{
return RCUserDefaults.standard
}
}