10.属性

摘要:
//2.必须将延迟存储属性声明成变量,因为属性的初始值可能在实例构造完成之后才会得到。//2.只有getter没有setter的计算属性就是只读计算属性。classStepCounter{vartotalSteps:Int=0{willSet{print;}didSet{print;}};}letstepCounter=StepCounter();stepCounter.totalSteps=200;stepCounter.totalSteps=360;stepCounter.totalSteps=900;/*打印结果:AbouttosettotalStepsto200Added200stepsAbouttosettotalStepsto360Added160stepsAbouttosettotalStepsto900Added540steps*/4.全局变量和局部变量计算属性和属性观察器所描述的功能也可以用于全局变量和局部变量。

属性将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。

存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性

另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。

1.存储属性

  • 简单的说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。
structFixedLengthRange
{
    var firstValue : Int;  //变量
    let length : Int;  //常量
}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3);
rangeOfThreeItems.firstValue = 3;
//rangeOfThreeItems.length = 4;  //错误,因为length是常量
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4);
//rangeOfFourItems.firstValue = 3;  //错误,因为结构体是值类型

//说明:当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。

在Swift中,有一种特殊的存储属性,叫延迟存储属性,类似于OC中的懒加载,通过关键字 lazy 来标示一个延迟存储属性。

classDataImporter
{
    var fileName = "data.txt";
}

classDataManager
{
    lazy var importer =DataImporter();
    var data =[String]();
}

let manager =DataManager();
manager.data.append("Some data");
manager.data.append("Some more data");  //到这里为止,DataImporter 实例的 importer 属性还没有被创建
print(manager.importer.fileName);  //DataImporter 实例的 importer 属性现在被创建了

//说明:
//1.使用lazy ,importer 属性只有在第一次被访问的时候才被创建。
//2.必须将延迟存储属性声明成变量(使用 var 关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
//3.如果一个被标记为 lazy 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
//4.当属性的值依赖于在实例的构造过程结束后才会知道影响值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。
//5.延迟存储属性必须初始化。

类比一下OC中的懒加载:

//实际上就是重写get方法
- (NSArray *)dataArray
{
    if (nil ==_dataArray){
        _dataArray =[NSArray array];
    }
    return_dataArray;
}

2.计算属性

  • 计算属性不直接存储值,而是提供一个getter 和一个可选的setter,来间接获取和设置其他属性或变量的值。
structPoint
{
    var x = 0.0;
    var y = 0.0;
}

structSize
{
    var width = 0.0;
    var height = 0.0;
}

structRect
{
    var origin =Point();
    var size =Size();
    
    //计算属性
var Center : Point{
        get{
            let centerX = origin.x + (size.width / 2);
            let centerY = origin.y + (size.height / 2);
            
            returnPoint(x: centerX, y: centerY);
        }
        set(value){
            origin.x = value.x - (size.width / 2);
            origin.y = value.y - (size.height / 2);
        }
    };
    
}

var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size( 10.0, height: 20.0));
square.Center = Point(x: 30.0, y: 40.0);

//说明
//1.如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 newValue。
//2.只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。只读计算属性的声明可以去掉 get 关键字和花括号。
//3.必须使用 var 关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。let 关键字只用来声明常量属性,表示初始化后再也无法修改的值。

3.属性观察器

  • 属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
  • willSet:在新的值被设置之前调用。观察器会将新的属性值作为常量参数传入,在willSet 的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称newValue 表示。
  • didSet:在新的值被设置之后立即调用。观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。如果在didSet 方法中再次对该属性赋值,那么新值会覆盖旧的值。
classStepCounter
{
    var totalSteps: Int = 0{
        willSet(newTotalStep){
            print("About to set totalSteps to (newTotalStep)");
        }
        didSet(oldTotalStep){
            print("Added (totalSteps - oldTotalStep) steps");
        }
    };
}

let stepCounter =StepCounter();
stepCounter.totalSteps = 200;
stepCounter.totalSteps = 360;
stepCounter.totalSteps = 900;

/*打印结果:
 
 About to set totalSteps to 200
 Added 200 steps
 About to set totalSteps to 360
 Added 160 steps
 About to set totalSteps to 900
 Added 540 steps
 */

4.全局变量和局部变量

  • 计算属性和属性观察器所描述的功能也可以用于全局变量和局部变量。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。
  • 全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记lazy修饰符。

5.类型属性

  • 类型属性用于定义某个类型所有实例共享的数据。存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算型属性一样只能定义成变量属性。
  • 跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。
  • 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用lazy 修饰符。
structSomeStructure
{
    static var storedTypeProperty = "Some value.";
    staticvar computedTypeProperty: Int {
        return 1;
    };
}

enumSomeEnumeration
{
    static var storedTypeProperty = "Some value.";
    staticvar computedTypeProperty: Int {
        return 6;
    };
}

classSomeClass
{
    static var storedTypeProperty = "Some value.";
    static let storedTypePropertyConstant = "Constant value.";
    staticvar computedTypeProperty: Int {
        return 27;
    };
    
    classvar overrideableComputedTypeProperty: Int {
        return 107;
    };
}

print(SomeStructure.storedTypeProperty)  //输出 "Some value."
SomeStructure.storedTypeProperty = "Another value."print(SomeStructure.storedTypeProperty)  //输出 "Another value."
print(SomeEnumeration.computedTypeProperty)  //输出 "6"
print(SomeClass.computedTypeProperty)  //输出 "27"

//说明
//1.在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
//2.使用关键字 static 来定义类型属性。在为类定义计算型类型属性时,可以改用关键字 class 来支持子类对父类的实现进行重写。

6.通过闭包或函数设置属性的默认值

  • 如果某个存储型属性的默认值需要一些定制或设置,可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。
  • 这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值。
  • 闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
classSomeClass
{
    let someProperty: Int ={
        let iTemp = 3;
        returniTemp;
    }();
}

免责声明:文章转载自《10.属性》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇visio 导出无边距的pdf 图【Go语言】连接数据库SQLite、MySQL、Oracle下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JAVA基础-栈与堆,static、final修饰符、内部类和Java内存分配

Java栈与堆 堆:顺序随意 栈:后进先出(Last-in/First-Out). Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为...

C语言入门阶段:教你几招如何牢记常量和变量

小伙伴们有没有想过什么是C常量,什么是C变量?为什么它们是编程世界的重要组成部分?在今天就带小伙伴一起学习和了解C语言中的常量和变量。 常量: 1、整型常量(整常数) 整型常量是整数类型的数据。可用以下三种形式表示,具体如下: 十进制整数,123,-456,0; 八进制整数,如0123,-011; 十六进制整数,如0x123,-0x12。 2、实型常...

WindowsAPI笔记(一)

句柄 在Windows应用程序中,窗口是通过窗口句柄(HWND)来标识的。我们要对某个窗口进行操作,首先就要得到这个窗口的句柄。句柄(HANDLE)是Windows程序中一个重要的概念,使用也非常频繁。在Windows程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时会为它们分配内存,并返回标识这些资源的标识号,即句柄。在后面的内容中我们...

iOS开发中 类、对象、实例变量、成员变量、属性变量等区别和关系

类(class):具有相同属性和行为等同一类元素等总称,类是一个抽象的概念。 区分是类还是对象,看它能否继续被细分。 在OC中,类是表示对象类型的结构体,对象通过类来获取自身的各种信息。类由两个部分组成:*.h和*.m文件组成。 *.m文件中 implemention部分是类的实现部分,内部包含类中的各种信息,包括各种实例方法或类方法。 类别( categ...

C++星号的含义

[转载] [http://blog.sina.com.cn/s/blog_4a50d85b0100uk3c.html]   1、乘法运算符   2、定义指针 int *p = 0; 还是 int* p = 0;? 后一种比较容易这样理解:定义了一个变量p,它是指针型的(更详细一点,是指向int的指针型),相比而言,前面一种定义似乎是定义了*P这个奇...

for循环中的闭包

1.代码 for(i=0;i<3;i++){ setTimeout(function(){ console.log(i); },0) } 输出为: 2.代码(闭包,直接执行匿名函数) for (var i = 0; i < 3; i++) { (function(x){ s...