propertye wrapped, optional在Swift妙用

2021-03-18 11:29:06 浏览数 (1)

1 关于Optional

使用swift开发项目中会用大所谓的可选类型,如下面的:

var age:Int?

我们做做业务是往往使用 if 或者 guard来走

代码语言:javascript复制

guard let age = age else {
    return
}

if let mAge = age  {
    
}

var b = age ?? 0

第三种的解包造成大量的'??', 对于接触一段时间swift就知道上面age的声明内部其实是一个Optional的类型,等价于:

var age:Optional<Int>

基于此我们是不是可以根据局这个思路读Optional机型一次扩展来消灭使用中的??判断

思路大致是:为数据类型设置默认值

代码语言:javascript复制

public protocol Letable  {
    static func defaultLetValue() -> Self
}

// 其他类型可以模仿此
extension String : Letable {
    public static func defaultLetValue() -> String {
        return ""
    }
}

这样我们就可以对Optionalzuo 泛型约束进行扩展

代码语言:javascript复制
public
extension Optional where Wrapped : Letable {
  var `let`:Wrapped {
        switch self {
        case .none:
            return Wrapped.defaultLetValue()
        case .some(let value):
            return value
        }
    }
}

public extension Optional {
    @inlinable
    func `let`(_  block: (Wrapped)->Void) {
        switch self {
        case .none:
            break
        case .some(let value):
            block(value)
        }
    }
}

我们看看怎么使用:

代码语言:javascript复制

var c:Bool? // c.let
let a:String? // a.let
 let aa:String?
   aa.let { (v) in
            XCTAssert(false, "此时不会执行的")
        }
        
var stu: Studnt? 
  stu.let { (v) in
            XCTAssertTrue(v is Studnt)
        }

2 关于链式

链式这个也是基于协议的扩展与泛型约束来使用,进行一些数据的扩展

代码语言:javascript复制


public
protocol Thenable {}

public
extension Thenable where Self : Any {
    
    func with(_ block: (inout Self)throws -> Void) rethrows -> Self {
        var copy = self
        try block(&copy)
        return copy
    }
    
    func `do`(_ block: (Self)throws -> Void) rethrows {
        try block(self)
    }
}


public
extension Thenable where Self: AnyObject {
    func then(_ block:(Self) throws -> Void) rethrows -> Self {
        try block(self)
        return self
    }
}



extension Array: Thenable {}
extension Dictionary: Thenable {}
extension Set: Thenable {}
extension NSObject: Thenable {}

#if os(iOS) || os(tvOS)
extension UIEdgeInsets : Thenable {}
extension UIOffset : Thenable {}
extension UIRectEdge: Thenable{}
#endif

#if !os(Linux)
extension CGPoint: Thenable {}
extension CGRect: Thenable {}
extension CGSize: Thenable {}
extension CGVector: Thenable {}
#endif

整体的代码量不多,但是很具有f鞥个

代码语言:javascript复制
let label =  UILabel().then {
            $0.textColor = .red
            $0.textAlignment = .center
        }
代码语言:javascript复制
 UserDefaults.standard.do {
            $0.set("aa", forKey: "aKey")
            $0.synchronize()
        }
 
代码语言:javascript复制
let rect =  CGRect(x: 0, y: 0, width: 0, height: 0).with {
            $0.origin.x = 100
            $0.size.width = 100
        }

3 属性包装器在Codable中的使用

属性包装器着实有点不同,具体使用大家百度科普,这里我们将其搬运到我们Codable中缩减避免我们Json数据解析问题。

COdable中不完美的一点是非Optional对应数据缺失往往会解析失败

对于这个问题我们可以仿照上文1 的方式设置类型默认值,外加属性包装器来解决

代码语言:javascript复制
public protocol DefaultValue {
    associatedtype Value: Codable
    static var defaultValue: Value { get }
}


@propertyWrapper
struct Default<T: DefaultValue> where T == T.Value {
    var wrappedValue: T.Value
    var value:T.Value?
    init(_ value: T.Value? = nil) {
        self.value = value
        if let value = value {
            wrappedValue = value
        } else {
            wrappedValue = T.defaultValue
        }
    }
    
}

我们首先定义可以修饰能被Codable的泛型属性包装器

代码语言:javascript复制
extension Default : Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
      try  container.encode(wrappedValue)
    }
}

extension Default: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
            wrappedValue = (try? container.decode(T.Value.self)) ?? T.defaultValue
    }
}


extension KeyedDecodingContainer {
    func decode<T>(
        _ type: Default<T>.Type,
        forKey key: Key
    ) throws -> Default<T> where T: DefaultValue {
        let value = (try decodeIfPresent(type, forKey: key))
        return value ?? Default(T.defaultValue)
    }
    
}

之后我们自定义属性包装器自身的Codable

到此是我们的属性包装器自身可Codable,同时又可修饰Codable,还能在key值缺失是使用泛型的默认值作为数据,一切看似都很完美了

到此我们即可实现大部分功能了

代码语言:javascript复制
struct Video: Codable {
    
    @Default var id: Int
    
    @Default(22) var age: Int
    
    @Default var title: String
    
    @Default(true) var commentEnabled: Bool
    
    }

但是我们还缺少一样实例Array,老规矩使用泛型约束来吧

代码语言:javascript复制

extension Array :DefaultValue where Element:DefaultValue , Element: Codable {
    public typealias Value = Array<Element>
    public static var defaultValue: Array<Element> {
        return []
    }
}

至此你可以这么使用了

代码语言:javascript复制
    @Default var list:[Person]

也学到此刻你觉得已经完美了,其实还有一个问题我们怎么处理Optional呢?---答案是扩展遵循协议啦

代码语言:javascript复制

extension Optional : DefaultValue where Wrapped: Codable, Wrapped:DefaultValue  {
    public typealias Value = Optional<Wrapped>
    public static var defaultValue: Optional<Wrapped> {
        return Optional.init(Wrapped.defaultValue as! Wrapped)
    }
}

至此基本的jiu完成啦

0 人点赞