如何写出更优雅的swift代码

背景

写此篇文章的背景是团队例行分享。
自从宝宝出生后,便很少花时间看技术上的东西,这点确实需要自我反省。
毕竟技术的提升,需要长期累积。

主要内容

开门见山,直接切入主题,今天主要介绍swift的一些关键词、高阶函数、属性封装器,
通过以上3个思路,可以帮助我们写出更优雅的swift代码。

1、defer

defer定义:一个函数在return前,会执行defer的block代码,
相当于@try {
} @catch (NSException *exception) {
} @finally {
}里面的finally的block一样的效果。

应用场景:

场景1:清理工作、回收资源

//关闭文件、数据库、关闭loading
func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // 处理文件。
        }
        // close(file) 会在这里被调用,即作用域的最后。
    }
}

场景2:减少冗余代码,以网络请求后的业务逻辑作为案例

//网络请求
func loadData(_ complete: ((Error?, [Any]?) -> ())?) {
    let request = NetRequest(path: "homepage")
    request.fetch { response in
        guard let dict = response as? [String: AnyObject] else {
            DispatchQueue.main.async {
                complete?(error, nil)
            }
            return
        }
        guard let success = dict["success"] as? bool, success == true else {
            DispatchQueue.main.async {
                complete?(error, nil)
            }
            return
        }
        guard let homelist = dict["homelist"] as? [String]? else {
            DispatchQueue.main.async {
                complete?(error, nil)
            }
            return
        }
        DispatchQueue.main.async {
            complete?(nil, homelist)
        }
    }
}

使用defer后,可以抽出冗余的代码,并简化成如此:

func loadData(_ complete: ((Error?, [Any]?) -> ())?) {
    let request = NetRequest(path: "homepage")
    request.fetch { response in
        var error: Error? = nil
        var data: [String]? = nil
        defer {
            //避免重复使用
            DispatchQueue.main.async {
                complete?(error, data)
            }
        }
        guard let dict = response as? [String: AnyObject] else {
            return
        }
        guard let success = dict["success"] as? bool, success == true else {
            return
        }
        guard let homelist = dict["homelist"] as? [String]? else {
            data = homelist
            return
        }
    }
}

2、高阶函数 Map、Filter、FlatMap、Reduce

一、 Map
定义:遍历集合,并对集合中的每个元素执行相同的操作。

//将[a, b, c]转换为[avalue, bvalue, cvalue]
let array = ["a","b","c"]
let array1 = array.map{ str in
    return str + "value1"
 }
let array2 = array.map {
    return $0 + "value2"
}
// print avalue,bvalue,cbalue,写法array1与array2是等价的。$0,$1表示闭包里的第1个,第2个参数

二、Filter
定义:遍历集合,返回包含满足条件的元素的数组

let array = ["a","b","c"]
let array1 = array.map{ str in
      return str + "value1"
}
let array2 = array.map {
       return $0 + "value2"
 }
 let array3 = array.filter{ str in
      return str != "b"
  }
  let array4 = array.filter{
       return $0 != "b"
  }
//print a,c

三、FlatMap
定义:数组集合降阶

var array = [[1,2,3],[6,7,8]]
var array1 = array.flatMap{$0}
//print: [1, 2, 3, 6, 7, 8]

四、Reduce
定义:将集合中的所有项组合起来,创建一个单一的值, 可以加减乘除,拼凑字符串等

//未使用 reduce,常规写法
var sum = 0  
let mArray = [1,2,3,4,5]  
for i in mArray {  
   sum = i+sum  
} 

//使用 reduce写法
var nums = [1, 2, 3, 4, 5]
var sum = nums.reduce(0) { $0 + $1 } // 15
//(0)表示初始值

3、属性包装器:@propertyWrapper

定义:用来修饰属性,抽取关于属性重复的逻辑来达到简化代码的目的

//未使用属性包装器的通常做法,如果有多个属性,就需要重复多个代码
class XLUtil {
    static var ccdguide: Bool {
        set {
            UserDefaults.standard.setValue(newValue, forKey: "abcdguide")
        }
        get {
            return UserDefaults.standard.bool(forKey: "abcdguide")
        }
    }
}
//使用属性包装器后,代码
class XLUtil {
    @XLUserConfigSecond("accdguide", false)
    static var accdguide
    
    @XLUserConfigSecond("accdguide2", false)
    static var accdguide2
    
    @XLUserConfigSecond("accdguide3", false)
    static var accdguide3
    
    @propertyWrapper
    struct XLUserConfigSecond<T> {
        var key: String
        var defaultValue: T
        
        init(_ key: String, _ defaultValue: T) {
            self.key = key
            self.defaultValue = defaultValue
        }
        
        var wrappedValue: T {
            get {
                guard let value = UserDefaults.standard.object(forKey: self.key) else { return defaultValue }
                return value as! T
            }
            
            set {
                UserDefaults.standard.setValue(newValue, forKey: self.key)
            }
        }
    }

//首字母大写
    @propertyWrapper struct Capitalized {
        var wrappedValue: String {
            didSet { wrappedValue = wrappedValue.capitalized }
        }

        init(wrappedValue: String) {
            self.wrappedValue = wrappedValue.capitalized
        }
    }
}
//泛型可以传入block函数
class XLAbcDataSource: NSObject {
    
    var _ccdModels: [XLACDMaterialModel]?
    var ccdModels: [XLACDMaterialModel] {
        get {
            if _ccdModels == nil {
                _ccdModels = reloadData()
            }
            return _ccdModels ?? []
        }
    }
    
    var _selectedACDModel: XLACDMaterialModel?
    var selectedACDModel: XLACDMaterialModel? {
        get {
            if _selectedACDModel == nil {
                _selectedACDModel = XLAbcDataSource.defaultMaterial()
            }
            return _selectedACDModel
        }
        set {
            _selectedACDModel = newValue
        }
    }
    
    static func reloadData() -> [XLACDMaterialModel]? {
        let allModels = [XLACDMaterialModel(),XLACDMaterialModel()]
        return allModels
    }
    
    ///默认素材
    static func defaultMaterial() -> XLACDMaterialModel? {
        return XLACDMaterialModel()
    }
    
    static let block = {
        return XLACDMaterialModel.reloadData()
    }
    @XLLazy(defaultValue: block()) var acdModels2
}

@propertyWrapper struct XLLazy<T> {
    var defaultValue: T?
    var value: T?
    
    init(defaultValue: T?) {
        self.defaultValue = defaultValue
    }
    
    var wrappedValue: T? {
        set {
            value = newValue
        }
        get {
            guard let value = self.value else {
                return defaultValue
            }
            return value
        }
    }
}

4、异步代码,同步执行:async,wait

第1个例子,下面的函数通过一个 回调 返回数值:

//常规异步,通过block回调
func fetchData(completion: @escaping ([String]) -> Void) {
    DispatchQueue.main.async {
        completion(["apple", "pear"])
    }
}
//使用async后可以用同步代码继续异步任务
func fetchData() async -> [String] {
    Sleep(3)
    Return ["apple", "pear"]
}
func fetch() async {
    let items = await fetchData()
    for item in items {
        print(item)
    }
}

第2个例子:通过加载一张图片来理解,加载图片共分为 4 个步骤:

  1. 生成网络请求;
  2. 向服务器发送请求,并等待服务器返回结果;
  3. 根据下发的 Data 构建 UIImage;
  4. 最后准备缩略图,并在完成时执行回调。

常规异步做法,如下所示:

func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
    let request = thumbnailURLRequest(for: id) 
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            completionHandler(nil, error)
        } else if (response as? HTTPURLResponse)?.statusCode != 200 {
            completion(nil, FetchError.badID)
        } else {
            guard let image = UIImage(data: data!) else {
                completion(nil, FetchError.badImage)
                return
            }
            image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
                guard let thumbnail = thumbnail else {
                    completion(nil, FetchError.badImage)
                    return
                }
                completion(thumbnail, nil)
            }
        }
    }
    task.resume()
}

使用async、wait后新流程,将异步的代码变成同步
1、创建缩略图URLRequest;
2、发送网络请求并接收服务端返回;
3、根据返回的 data 创建 UIImage;
4、返回获取到的缩略图。

func fetchThumbnail(for id: String) async throws -> UIImage {
    let request = thumbnailURLRequest(for: id)
    let (data, response) = try await URLSession.shared.data(for: request)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw FetchError.badID }
    let maybeImage = UIImage(data: data)
    guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
    return thumbnail
}

5、@globalActor actor MainActor

后续介绍

6、associatedtype 协议泛型

后续介绍
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容