背景
写此篇文章的背景是团队例行分享。
自从宝宝出生后,便很少花时间看技术上的东西,这点确实需要自我反省。
毕竟技术的提升,需要长期累积。
主要内容
开门见山,直接切入主题,今天主要介绍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 个步骤:
- 生成网络请求;
- 向服务器发送请求,并等待服务器返回结果;
- 根据下发的 Data 构建 UIImage;
- 最后准备缩略图,并在完成时执行回调。
常规异步做法,如下所示:
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 协议泛型
后续介绍