《Swift必备Tips》读书笔记

栏目: Swift · 发布时间: 5年前

内容简介:本文是对不断更新中~mutating关键字是用来在方法中修改struct或enum变量的,若不声明会报错。

本文是对 喵神 编写的 《Swift必备Tips(第四版)》 的读书笔记,内容是我不太熟悉或觉得有用的Swift使用技巧。

不断更新中~

将protocol的方法声明为mutating

mutating关键字是用来在方法中修改struct或enum变量的,若不声明会报错。

static和class 关键字

异同点

相同点:static和class都是表示“类型范围作用域”这一概念的。

不同点:class是专门用在class类型的上下文中的,可以用来 修饰类方法和类属性。

class中现在是不能出现在class的存储属性的。

class MyClass{
	class varbar:Bar?
}

会得到一个编译错误,class variables not yet supported,改成static就可以通过编译了。

结论

任何时候使用static都是没问题的。

多类型和容器使用Any/AnyObject的技巧

Swift中常用的原生容器类型有 Array/Dictionay/Set ,他们都是范型的,也就是说放在一个集合中的类型要一致。

但是Any可以让我们在容器中放各种类型的元素,但也不可避免的带来了 部分信息损失 ,从容器中取出后还需要进行一次类型转换。

// Any 类型可以隐式转换
let mixed: [Any] = [1, "two", 3]

// 转换为 [NSObject]
let objectArray = [1 as NSObject, "two" as NSObject, 3 as NSObject]

//建议的使用方式
let mixed: [CustomStringConvertible] = [1, "two", 3]

for obj in mixed {
    print(obj.description)
}

改善方法一

作者认为,把这些不同类型的元素放到一个容器中,肯定是由于他们有 共性 ,也就是这些元素服从某个共同的协议,这样虽有一定损失,但相对于Any或AnyObject还是改善了不少。

改善方法二

另一种做法是用enum可以带有值的特点,将类型信息 封装到enum中

enum IntOrString{
    case IntValue(Int)
    case StringValue(String)
}

let mixed = [IntOrString.IntValue(1),
             IntOrString.StringValue("two"),
             IntOrString.IntValue(3)]

for value in mixed {
    switch value {
    case let .IntValue(i):
        print(i * 2)
    case let .StringValue(s):
        print(s.capitalized)
    }
}

AnyClass

除了上面提到的Any和AnyObject,还有一个表示 任意 这个概念的东西 AnyClass 。在Swift中的定义方式:

typealias AnyClass = AnyObject.Type

得到的是一个元类型(Meta),存储的是一个类的类型本身。除了可以用元类型来调用类方法或类变量,还可以用在 传递类型的时候,不需要不断地改动代码了。

	let usingVCTypes: [AnyClass] = [MusicViewController.self,
    AlbumViewController.self]

func setupViewControllers(_vcTypes: [AnyClass]) {
    for vcType in vcTypes {
        if vcType is UIViewController.Type {
            let vc = (vcType as! UIViewController.Type).init()
            print(vc)
        }

    }
}

setupViewControllers(usingVCTypes)

在编框架时,搭好框架后,用DSL的方式进行配置,就可以在不触及Swift编码的情况下,完成一系列复杂操作了。另外,Cocoa API中,也经常需要一个AnyClass的输入,如:self.tableView.registerClass(

UITableViewCell.self, forCellReuseIdentifier: “myCell”)

Self

首字母大写的Self,通常用在协议内,指代实现协议的类型 和子类 。实现代理方法有点技巧。

protocol Copyable{
    func copy() -> Self
}

class MyClass:Copyable{

    var num = 1

    func copy() -> Self {
        let result = type(of: self).init()
        result.num = num
        return result
    }

    required init() {

    }
}

直接使用MyClass()进行初始化是错误的,无法编译,因为该方法要求返回的是一个 抽象的 、表示当前类型的Self,而不是真实类型MyClass。type(of:)在js中也有,保证了方法与当前类型上下文无关,无论是MyClass还是它的子类,都可以正确地返回合适的类型,满足Self的要求。

注意需要有required关键字修饰init方法, 保证当前类和子类都能响应init方法 ,否则type(of: self).init()可能会出错。另一个解决方法是申明class为 final ,保证不会有其他子类来继承这个类型,本质上也是保证type(of: self).init()不会出错。

动态类型和多方法

Swift默认是不采用动态派发的,方法调用在编译时决定。如果想绕开这个限制,需要手动对输入类型做判断与转换。如果没有判断,即使printThem的函数第一个参数传入Dog(),也是派发的Pet的printPet()方法。

func printThem(_pet: Pet,_cat: Cat) {
    if let aCat = pet as? Cat {
        printPet(aCat)
    } else if let aDog = pet as? Dog {
        printPet(aDog)
    }
    printPet(cat)
}

final 关键字

上面提到了final,作者对final关键字的态度是: 虽然final能告诉编译器这段代码不会被更改,但是提升的性能非常有限,建议先优化算法和图像相关的内容。

final真正适用的几个场景:

  • 类方法或功能以及确实完备了,比如很难会重写计算字符串MD5或AES加密解密的 工具 类。
  • 子类继承和修改是一件危险的事情,比如在某个公司管理的系统中我们对员工按照一定规则进行编号,这样通过编号我们能迅速找到任一员工。而假如我们在子类中重写了这个编号方法,很可能就导致基类中的依赖员工编号的方法失效。
  • 为了父类中某些代码一定会被执行。比如有时候父类中有一些关键代码是在被继承重写后必须执行的 (比如状态配置,认证等等),否则将导致运行时候的错误。

lazy 关键字

在构建和生成新的对象时,内存分配会在运行时耗费不少时间,如果有一些对象的属性和内容非常复杂,耗时不可忽略;另外,有些情况下,我们不会立即使用到一个对象的所有属性,默认初始化会将全部变量初始化,包括特定环境下的存储属性,这是一种浪费,懒加载就是为此而生。

lazy除了可以修饰属性,也有其他用法:

func lazy<S : SequenceType>(s: S) -> LazySequence<S>

func lazy<S : CollectionType where S.Index : RandomAccessIndexType>(s: S)
                -> LazyRandomAccessCollection<S>

func lazy<S : CollectionType where S.Index : BidirectionalIndexType>(s: S)
                -> LazyBidirectionalCollection<S>

func lazy<S : CollectionType where S.Index : ForwardIndexType>(s: S)
                -> LazyForwardCollection<S>
                
let data = 1...3
let result = data.lazy.map {
    (i: Int) -> Int in
    print("正在处理\(i)")
    return i * 2
}

print("准备访问结果")
for i in result {
    print("操作后结果为\(i)")
}

print("操作完毕")

用来配合像map和filter这类接受闭包并进行运行的方法一起,让整个行为变成延时的。有lazy和无lazy的输出是完全不同的。 对于那些不需要完全运行,可能提前退出的情况,使用lazy是进行性能优化的一种有效手段。


以上所述就是小编给大家介绍的《《Swift必备Tips》读书笔记》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Unity 3D游戏开发(第2版)

Unity 3D游戏开发(第2版)

宣雨松 / 人民邮电出版社 / 2018-9 / 89.00元

Unity 是一款市场占有率非常高的商业游戏引擎,横跨25 个主流游戏平台。本书基于Unity 2018,结合2D 游戏开发和3D 游戏开发的案例,详细介绍了它的方方面面,内容涉及编辑器、游戏脚本、UGUI 游戏界面、动画系统、持久化数据、静态对象、多媒体、资源加载与优化、自动化与打包等。 本书适合初学者或者有一定基础的开发者阅读。一起来看看 《Unity 3D游戏开发(第2版)》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具