美文网首页
如何写出更优雅的swift代码

如何写出更优雅的swift代码

作者: zhengxiaolang | 来源:发表于2022-07-06 13:38 被阅读0次

背景

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

主要内容

开门见山,直接切入主题,今天主要介绍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 协议泛型

后续介绍

相关文章

  • 如何写出更优雅的swift代码

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

  • 如何写出优雅的代码?

    本文仅仅是对《代码整洁之道》摘录: 简单代码,重要顺序:1.能通过所有的测试2.没有重复代码3.体现系统中的全部设...

  • 如何写出优雅的代码?

    一段代码的作者的责任不应该仅仅是把代码写出来,测试上线。还应该包含完整的单元测试,经过代码复查,并进而上线运行发挥...

  • 如何写出优雅的代码

    有的人写出的代码让人看上去赏心悦目,心情舒畅,但是有的人写的代码让人觉得繁琐而且很乱让人心烦意乱,这两种体验真是天...

  • 优雅代码的秘密,都藏在这6个设计原则中

    优雅的代码,犹如亭亭玉立的美女,让人赏心悦目。而糟糕的代码,却犹如屎山,让人避而远之。 如何写出优雅的代码呢?那就...

  • java新手学习,如何避免自己写的代码成为别人眼中的一坨屎!

    普通的软件工程师堆砌代码,优秀的软件工程师优雅代码,卓越的软件工程师简化代码。如何写出优雅整洁易懂的代码是一门学问...

  • 如何写出优雅的Javascript代码

    笔者以前写Javascript的时候,经常会把代码写的又臭又长又啰嗦。 在经过一段实践的实践和反思后,代码的优雅性...

  • 如何写出优雅的C代码

    一直以来,每每翻看自己写的代码,越看越不顺眼。不是指逻辑上的,就纯粹是外观上的排版以及命名等。这就导致我没事就调整...

  • 如何优雅的写出Android开发代码

    很多时候我们去面试,人家总会问一个问题,你们公司开发一个app是如何进行技术选择的,app中涉及到了哪些开发模式,...

  • 如何写出优雅的前端代码

    最近越来越觉得自己项目中的代码结构很差,收集这篇文章,希望可以时刻提醒自己注意代码的可维护性 项目结构 JAVAS...

网友评论

      本文标题:如何写出更优雅的swift代码

      本文链接:https://www.haomeiwen.com/subject/yioubrtx.html