# Day10--只有蓝图是不够的!我们要来变出一个实体!

爲什麽需要初始化?

Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that’s required before the new instance is ready for use.

初始化是一种过程,准备类别、结构或枚举的使用。这个过程涉及设定一个初始化值给储存属性以及实体,和展示任何其他的设定或初始化,这是在生成一个实体前必要存在的事情。

在官方网站上大致上是这样去诠释初始化这个词的,不过,有一个更精确的说法是:「类和结构必须在创建该类或结构的实例时将其所有存储的属性设置为适当的初始值。存储的属性不能处於不确定状态。

也就是说,其实初始化这件事情的本身,就是为了让「储存属性」的状态被确定。

我们确定了为什麽要初始化之後,接着要讨论的事情也就围绕在初始化器上了。

初始化器最基本的样子:

init(){
   //里面是要初始化的内容
}
  1. 默认初始化器(Default Intializer)
struct SomeStruct{
    var someText = "Text"
}

如上面所述,这样就是已经内含的默认初始化器,因为我们直接把值指派给了一个字串。

而,初始化这件事情除了默认之外,其实也可以是客制的,下面这边会稍加描述:

  1. 客制初始化器(Customizing Intailizer)
struct SomePerson{
    var name:String
    var height:Int
    var weight:Int
    init(name:String ,height:Int ,weight:Int){
        self.name = name
        self.height = height
        self.weight = weight
    }
}

上面的初始化器中,就很明显的定义了在这个结构中,若你要使用这个结构实体化後的结果,就必须满足在它的参数中所要求的项目。

而初始化器其实也跟函式相似,可以提供它内部参数与外部参数:

struct BMIStruct{
    var BMI:Int
    init(fromHeight height:Int, fromWeight weight:Int){
    BMI = weight / (height/100)*(height/100)
    }
    init(fromKenHeight KenHeight:Int ,fromKenWeight KenWeight:Int){
    BMI = KenWeight / (KenHeight/100)*(KenHeight/100)
    }
}
let KenBMI = BMIStruct(fromKenHeight:170, fromKenWeight:60)
print(KenBMI.BMI)

然而,上述基本上是Struct的初始化器,Class的初始化其实在初始化器的样子、规则其实跟Struct一样,都是在赋予值与否的问题。

然而,Class跟Struct的初始化器其实拥有两个很大不同之处在於:
class具有继承(Inheritance),而Struct并没有,所以这让class的初始化更具有些许复杂性。

反初始化

引自《The Swift of Programming Language》中文版:

析构原理

在一个类别的实例被释放之前,析构函式被立即呼叫。用关键字deinit来标示析构函式,类似於初始化函式用init来标示。析构函式只适用於类型别。
Swift 会自动释放不再需要的实例以释放资源。如自动引用计数那一章描述,Swift 通过自动引用计数(ARC)处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。

100Days of Swift的文章亦指出:

The job of deinitializers is to tell us when a class instance was destroyed. For structs this is fairly simple: the struct is destroyed when whatever owns it no longer exists. So, if we create a struct inside a method and the methods ends, the struct is destroyed.

析构器的工作是告诉我们什麽时候一个类别实例会被破坏。对於结构来说,这非常的简单:结构的废弃是当我们的拥有者再存在,所以,如果我们创造了一个方法在结构里面,而方法结束後,结构也随之废弃。

Behind the scenes Swift performs something called automatic reference counting, or ARC. ARC tracks how many copies of each class instance exists: every time you take a copy of a class instance Swift adds 1 to its reference count, and every time a copy is destroyed Swift subtracts 1 from its reference count. When the count reaches 0 it means no one refers to the class any more, and Swift will call its deinitializer and destroy the object.

在萤幕後,Swift呈现了某件事情:自动参考计数,或称之为ARC,ARC的功能是在追踪有多少个类别的实例存在,一旦你复制出一个实例,ARC就会自动的在参考计数上加一,以及每次一个复制废弃後,Swift就会从实例计算中减去1。当计算达到零时,意指没有对象可从类别中再找到东西了,Swift就会呼叫析构器去废弃物件。

总而言之,析构在其目的上,是为了将不需要的实例资源释放。而Swift有它自己的实例内存管理的方法,通常实例产生时,不会需要开发者动手去清理,但我们仍有可能碰到需要清理的时候,尤其是我们使用自己的资源,而常见者如:

如果创建了一个自定义的类别来打开一个文件,并写入一些资料,你可能需要在类别实例被释放之前关闭该文件。

上述之文字所及,也就是说,析构的使用,是基於类别实体被释放前的运作机制,我们通常创建了一个类别後,底下会有建构器,但为了避免资料一直处於打开状态,所以希望在建构器後,能将那些引入的资料关闭,这个时候,就需要析构器来扮演这样的角色。

析构器

在类别的定义中,每个类别最多只能有一个析构函式。析构函式不带任何参数,在写法上不带括号:

deinit {
    // 执行析构过程
}

析构函式是在实例释放发生前一步被自动呼叫。

因此,析构器有一些特点:

不允许主动呼叫自己的析构函式。
子类别继承了父类别的析构函式,并且在子类别析构函式实作的最後,父类别的析构函式被自动呼叫。

即使子类别没有提供自己的析构函式,父类别的析构函式也总是被呼叫。

因为直到实例的析构函式被呼叫时,实例才会被释放,所以析构函式可以存取所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件的名称)。
就以下列举例来说:

其实析构器就是在类别结束的时候,作为是将整个结构的内存清空的一种存在。

总之,关於析构器这件事情,在100Days of Swift中Paul Hudson在文章的结尾处是这样说的:

So, the simple reason for why structs don’t have deinitializers is because they don’t need them: each struct has its own copy of its data, so nothing special needs to happen when it is destroyed.

所以,结构不需要析构器的最简单理由,是因为他们不需要它:每个结构都有自己拥有的复制的资料,所以当它们被销毁的时候,没有什麽特别需要注意的(这里指结构为什麽没有析构器)

You can put your deinitializer anywhere you want in your code, but I love this quote from Anne Cahalan: “Code should read like sentences, which makes me think my classes should read like chapters. So the deinitializer goes at the end, it’s the ~fin~ of the class!”

翻译翻译:你可以让你的析构器随便放置,但我比较倾心於Anne Cahalan所说的:程序码应该读起来像句子,这样让我觉得我的类别读起来像是读一个篇章。所以,析构器放置在最底,就像是:这个篇章的结尾~

tags: 铁人赛

<<:  自动化测试,让你上班拥有一杯咖啡的时间 | Day 11 - 如何合并测试报告

>>:  [区块链&DAPP介绍 Day17] Solidity 教学 - using-for

Day32:php(1)

PHP(全称:PHP:Hypertext Preprocessor,即「PHP:超文字预处理器」)是...

资安学习路上-渗透测试实务3

漏洞利用 利用侦测到的现有漏洞取得初始控制权 上图取自台科大资安社课教材 1. Exploit-db...

Ubuntu - Ubuntu 查看 CPU 温度

Ubuntu - Ubuntu 查看 CPU 温度 参考资料 网址如下: How to Get CP...

Day 7 Odoo的Tree View

Odoo模组开发实战 目录 List View 第一章 List View 增加显示'使用人'栏位 ...

DAY 20 - 四足战车 (1)

大家好~ 我是五岁~ 30天发文即将迈入最後1/3哩~ 所以从今天开始要挑战画更难的东西~! 目标 ...