延时加载或者说延时初始化是很常用的优化方法,在构建和生成新的对象的时候,内存分配会在运行时耗费不少时间,如果有一些对象的属性和内容非常复杂的话,这个时间更是不可忽略。另外,有些情况下我们并不会立即用到一个对象的所有属性,而默认情况下初始化时,那些在特定环境下不被使用的存储属性,也一样要被初始化和赋值,也是一种浪费。

在其他语言 (包括 Objective-C) 中延时加载的情况是很常见的。我们在第一次访问某个属性时,判断这个属性背后的存储是否已经存在,如果存在则直接返回,如果不存在则说明是首次访问,那么就进行初始化并存储后再返回。这样我们可以把这个属性的初始化时刻推迟,与包含它的对象的初始化时刻分开,以达到提升性能的目的。以 Objective-C 举个例子 (虽然这里既没有费时操作,也不会因为使用延时加载而造成什么性能影响,但是作为一个最简单的例子,可以很好地说明问题):

// ClassA.h
@property (nonatomic, copy) NSString *testString; // ClassA.m
- (NSString *)testString {
if (!_testString) {
_testString = @"Hello";
NSLog(@"只在首次访问输出");
}
return _testString;
}

在初始化 ClassA 对象后,_testString 是 nil。只有当首次访问 testString 属性时
getter 方法会被调用,并检查如果还没有初始化的话,就进行赋值。为了方便确认,我们还在赋值时打印了一句 log。我们之后再多次访问这个属性的话,因为 _testString 已经有值,因此将直接返回。

在 Swift 中我们使用在变量属性前加 lazy 关键字的方式来简单地指定延时加载。比如上面的的代码我们在 Swift 中重写的话,会是这样:

class ClassA {
lazy var str: String = {
let str = "Hello"
print("只在首次访问输出")
return str
}()
}

我们在使用 lazy 作为属性修饰符时,只能声明属性是变量。另外我们需要显式地指定属性类型,并使用一个可以对这个属性进行赋值的语句来在首次访问属性时运行。如果我们多次访问这个实例的 str 属性的话,可以看到只有一次输出。

为了简化,我们如果不需要做什么额外工作的话,也可以对这个 lazy 的属性直接写赋值语句:

lazy var str: String = "Hello"

相比起在 Objective-C 中的实现方法,现在的 lazy 使用起来要方便得多。

另外一个不太引起注意的是,在 Swift 的标准库中,我们还有一组 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>

这些方法可以配合像 map 或是 filter 这类接受闭包并进行运行的方法一起,让整个行为变成延时进行的。在某些情况下这么做也对性能会有不小的帮助。例如,直接使用 map 时:

let data = 1...3
let result = data.map {
(i: Int) -> Int in
print("正在处理 \(i)")
return i * 2
} print("准备访问结果")
for i in result {
print("操作后结果为 \(i)")
} print("操作完毕")

这么做的输出为:

// 正在处理 1
// 正在处理 2
// 正在处理 3
// 准备访问结果
// 操作后结果为 2
// 操作后结果为 4
// 操作后结果为 6
// 操作完毕

而如果我们先进行一次 lazy 操作的话,我们就能得到延时运行版本的容器:

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("操作完毕")

此时的运行结果:

// 准备访问结果
// 正在处理 1
// 操作后结果为 2
// 正在处理 2
// 操作后结果为 4
// 正在处理 3
// 操作后结果为 6
// 操作完毕

对于那些不需要完全运行,可能提前退出的情况,使用 lazy 来进行性能优化效果会非常有效。

【iOS】Swift LAZY 修饰符和 LAZY 方法的更多相关文章

  1. Swift lazy 修饰符和方法

    LAZY 修饰符和 LAZY 方法 由 王巍 (@ONEVCAT) 发布于 2015/10/07 延时加载或者说延时初始化是很常用的优化方法,在构建和生成新的对象的时候,内存分配会在运行时耗费不少时间 ...

  2. 037——VUE中表单控件处理之表单修饰符:lazy/number/trim

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Vue 修饰符once的方法使用

    once:只执行一次 代码: <!doctype html> <html lang="en"> <head> <meta charset= ...

  4. Vue表单修饰符(lazy,number,trim)

    lazy:使用了这个修饰符将会从“input事件”变成change事件进行同步 <div id="example"> <input type="text ...

  5. iOS Swift结构体与类的方法调度

    前言 hello,小伙伴们:在忙碌中闲暇之余给大家聊聊swift的知识点,今天给大家带来的是swift中结构体与类的方法调度详细区别,希望对你有所帮助,好了废话不用多说,接下来步入主题! 1.普通方法 ...

  6. iOS之属性修饰符 retain、strong和copy区别测试

    时不时会有点迷惑属性修饰符retain.strong.copy三者之间的区别,还是把测试过程记录下来好一点! 1.属性修饰符结论 2.给retain.strong.copy修饰的字符串属性赋值指针变化 ...

  7. final修饰符(5)-final方法

    final修饰的类的方法不能被重写,例如如果父类不希望子类重写某个方法,则可以使用final修饰符修饰该方法 在java的Object类里面有一个final方法:getClass(),因为Java不希 ...

  8. iOS 实例变量修饰符

    @public 可以在其他类中访问被@public修饰的成员变量 可以在本类中访问被@public修饰的成员变量 可以在子类中访问fl中被@public修饰的成员变量 @private 不可以在其他类 ...

  9. ios swift 2 新的OptionSetType使用方法

    http://www.rockhoppertech.com/blog/swift-2-optionsettype/?utm_source=tuicool 主要使用方法如下 components([NS ...

随机推荐

  1. 一次断电后docker问题的排解

    这篇文章是一次真实的排查经历,可能对大多数人没什么作用,慎看. 断电后来电,服务器启动,手工启动各个docker,大部分正常,小部分还是出错,错误信息: <html> <head&g ...

  2. 新的一年新的变化!IT的大变天

    今天是一个特别的日子,祝女神朋友们,节日快乐,早点下班! 新的一年,大家又忙碌在加班加点的堆代码中,bug的陪伴使我快乐使我忧伤,想想想,也奋斗了五六百的岁月,实习期向往大城市的公司,梦想着有一天与自 ...

  3. [SDOI2015]星际战争

    水题啦 网络流+二分 误差才10^-3,乱搞直接开longlong暴力每个都乘1000,输出时除一下就好了 # include <bits/stdc++.h> # define IL in ...

  4. 踩坑系列の Oracle dbms_job简单使用

    二话不说先上代码 --创建存储过程 create or replace procedure job_truncateState is begin --此处就是要定时执行的sql execute imm ...

  5. Type Archive for required library: 'C:/Users/EuphemiaShaw/.m2/repository/org/apache/hadoop/hadoop-hdfs/2.6.5/hadoop-hdfs-2.6.5.jar' in project 'mapreduce' cannot be read or is not a valid ZIP file

    error: Description Resource Path Location Type Archive for required library: 'C:/Users/EuphemiaShaw/ ...

  6. javaScript执行环境、作用域链与闭包

    一.执行环境 执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为:每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中.虽然我们编写的代码无法访问这个对象 ...

  7. Redis 桌面管理器

    使用Redis桌面管理器,可以方便开发人员进行开发测试,对Redis存储内容进行可视化管理. 下载安装:https://redisdesktop.com/download 1. 为了方便测试,打开re ...

  8. eclipse的注释

    版权声明:本文为博主原创文章,转载请注明出处. 如果能帮助你,那我的目的就达到了 Window --> Java --> Code Style --> Code Templates ...

  9. 利用Java泛型实现简单的泛型方法

    问题:编写一个泛型方法add(),当传入不同数字类型的值时,能够进行加法运算,(如可以传入int.long.float.double类型,但要对传入的值做一定的限定,如必须是数字). 解决:利用泛型编 ...

  10. 设计模式——模板模式(C++实现)

    模板模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.   模板模式通过把不变的行为搬移到超类,去除子类中的重复代码来 ...