你真的了解构造过程吗(构造过程和析构过程)

注:本文为作者整理 , 尽量没有废话,都是干货 。希望看官们可以有所收获。

1、构造过程

构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务,在类中声明一个存储型变量 ,必须为其赋初始值 。可以通过构造器为其赋值

当我们创建一个类或者结构体的时候 系统默认给我们创建一个无参数的构造器 ,语法如下 :

init() {
    //可以在这里给存储型变量赋初始值
}
class Student{
    var name:String
    init(){
        name = "zhangsan "
    }
}

这里我们在构造方法中为name赋了初值 ,当然你也可以直接var name:String=“zhangsan” 定义的时候指定一个初始值
你可以通过输入参数和可选属性类型来定制构造过程,也可以在构造过程中修改常量属性

class Student1{
    var name:String
    let age:Int
     init(name:String){
       self.age = 20
       self.name = name
    }
    init(){
        self.age = 30
        self.name = "zhangsan"
    }
    init(nickName name:String){
        self.age = 40
        self.name = "Nike Name:\(name)"
    }
}

这个类中我们定义了三个构造方法 ,你可以根据需要选择调用任何一个

let st = Student1()
print(st.name)

let st1 = Student1(name: "lisi")
print(st1.name)

let st2 = Student1(nickName: "wangwu")
print(st2.name)
//结果:
//zhangsan
//lisi
//Nike Name:wangwu

swift 会为每个构造器自动添加外部参数名 ,相当于唯一符号,也可以自己制定外部参数名 。对外部参数名不了解的可以去看Swift详解之三———-函数(你想知道的都在这里)
如果你给一个变量声明位可选类型 , swift会默认赋为 nil ,证明这是一个在逻辑上允许为空得存储型属性var tele:Int?

在一个结构体中,不仅有默认的无参数构造器 , 系统还有声明一个逐一成员构造器

struct MySize {
    var width = 0.0,height = 0.0
}
var size = MySize(width: 27.3,height: 45.2)    //系统会自动给结构体创建一个逐一成员构造器。

如果在结构体中 ,自己显示指定了构造器 ,将不能再调用逐一构造和默认无参数构造

struct MySize1 {
    var width = 0.0,height = 0.0

    init(width:Double){
        self.width = width ;
        height = 2*width ;
    }
// init(){
// 
// }
}
//var size1 = MySize1(width: 27.3,height: 45.2)  //报错 extra argument 'height' in call
//var size1 = MySize()  //invalid redeclaration of 'size1'
var size1 = MySize1(width: 27.3)  //只能通过这种方式

不过可以自己手动再添加那两个 。

  • 指定构造器和便利构造器

这个算构造器这块比较绕的地方 ,有涉及到继承重载的一些东西 。这些都是面向对象的基础 ,这里不再详细说这些 ,做过任何一门面向对象语言的应该都回比较了解 。后面有时间应该会专门写一设计模式的模块 。

类里面的所有存储型属性–包括所有继承自父类的属性–都必须在构造过程中设置初始值。

指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。(我们上面例子的构造器 都是指定构造器 )

便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清、晰明。

一下说了这么多概念 ,看着都头晕 ,总结下就是 指定构造器可以根据父类链往上调用父类的构造器 , 遍历构造器调用同一个类中的指定构造器。。就这两句重要的。

我们来看个例子

class Book{
    var name:String
    init(name:String){
        self.name = name
    }
    convenience init(){
        self.init(name:"Swift全解析")
    }
}

我们这里也提供了一个没有参数的构造器init(),这个init()构造器为新实例提供了一个默认的占位名字”Swift全解析” ,这个就是便利构造器 ,前面用关键字 convenience 声明 。只能调用本类中的指定构造器 。

var swift = Book()
print(swift.name) //Swift全解析
var s  = Book(name:"object-c")
print(s.name) //object-c

这个就不用解释了。

我们来搞一个子类来提供书本的数量 。

class CountBook:Book{
    var bookCount:Int
    init(name:String,count:Int){
        self.bookCount = count
        super.init(name:name);
    }
    override convenience  init(name:String){
        self.init(name:name,count:1);
    }

}

这里在指定构造器中传入两个参数 ,并且往上调用了父类的构造器 。遍历构造器 ,重写了父类的构造器 ,并且调用了本类的指定构造器 。

let cb2 = CountBook(name:"精通 Swift ")
print("\(cb2.bookCount),\(cb2.name)") //1,精通 Swift

//上面是调用CountBook的便利构造器 count给了初始值所以这里count是1 
let cb1 = CountBook()
print(" \(cb1.bookCount),\(cb1.name)") // 1,Swift全解析
//这里估计有些人不太理解了,你看父类 ,init() 是不是init(name:String) 的便利构造器 ,然后子类重写了init(name:String) 又加了count得初始化 。。明白了吧!!

看了这些例子我们再来看下官方给的一些概念性的东西 。

指定构造器和便利构造器
类里面的所有存储型属性–包括所有继承自父类的属性–都必须在构造过程中设置初始值。
Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。
指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。

便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例
你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清、晰明。
构造器链
为了简化指定构造器和便利构造器之间的调用关系,Swift 采用以下三条规则来限制构造器之间的代理调用:
1. 指定构造器必须调用其直接父类的的指定构造器。
2. 便利构造器必须调用同一类中定义的其它构造器。
3. 便利构造器必须最终以调用一个指定构造器结束。
(指定构造器必须总是向上代理
便利构造器必须总是横向代理)

两段式构造过程
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。

两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问;也可以防止属性被另外一个构造器意外地赋予不同的值。

Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:

安全检查 1

指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。

如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。

安全检查 2

指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。

安全检查 3

便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。

安全检查 4

构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用self的值。

以下是两段式构造过程中基于上述安全检查的构造流程展示:

阶段 1

某个指定构造器或便利构造器被调用;
完成新实例内存的分配,但此时内存还没有被初始化;
指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化;
指定构造器将调用父类的构造器,完成父类属性的初始化;
这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部;
当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段1完成。
阶段 2

从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。

构造器的继承和重载
跟 Objective-C 中的子类不同,Swift 中的子类不会默认继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误的用来创建子类的实例。

在重载构造器时你必需使用关键字override。即使你重载的是默认构造器 也要加上override (重载属性 方法 下标脚本的时候都要加上override 关键字 )

看完这段话,云里雾里的。。

class Person {
    var name = ""
}

class Teacher:Person {

    var age:Int
    override init(){
        age=10           //如果需要对自己的属性进行初始化 在super之前
        super.init();  //必须先调用父类的构造 对父类属性进行舒适化
        name = "lisi"  //这里对父类的属性重新初始化
    }
}

这个例子包含了初始化自己,调用父类构造器 初始化父类属性的顺序 。是不是很清楚明了。

  • 自动构造器的继承

子类不会默认继承父类的构造器。但是如果特定条件可以满足,父类构造器是可以被自动继承的
1、如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。

2、如果子类提供了所有父类指定构造器的实现–不管是通过规则1继承过来的,还是通过自定义实现的–它将自动继承所有父类的便利构造器。

这个不用过多解释了吧,前面的例子都有 很好理解 、

  • 可失败构造器

如果一个类或者结构体在构造过程中可能会失败,为其定义一个可失败的构造器是非常有必要的,语法 init? 注意:可失败的参数名和参数类型不能和其他非可失败的相同 通过return nil 来表示可失败构造器在什么情况下失败

来看一个例子

class Animal{
    var name:String
    init?(name:String) {
        self.name = name
        if (name.isEmpty){
 return nil
        }
    }
}
let sheep = Animal(name: "sheep")
print(sheep?.name) //Optional("sheep")

let dog = Animal(name: "")
print(dog?.name) //nil

当然你也可以在链式构造器(便利构造 指定构造等) 中使用可失败构造器 ,也可以早重载可失败构造器

  • 必要构造器

在类的构造前面 加required 表明所有该类的子类都必须实现该构造器 。

class Book1{
    var  name : String
    required init(name:String){
        self.name = name
    }
}


class swiftBook:Book1 {

}
//let sw = swiftBook() //missing argument for parameter 'name' in call 不能这样调用啦,必须加参数
  • 通过闭包来设置默认值

意闭包结尾的大括号后面接了一对空的小括号。这是用来告诉 Swift 需要立刻执行此闭包。如果你忽略了这对括号,相当于是将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性

如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允许。同样,你也不能使用隐式的self属性,或者调用其它的实例方法。

class Test {

    var dog = "dog"

    var name:String = {
        var n = "zs"
        //在这里不能使用self.dog
        return n.uppercaseString
    }()

}

let t = test()
print(t.name)  //ZS

2、析构过程

在一个类的实例被释放之前,析构函数被立即调用。用关键字deinit来标示析构函数,类似于初始化函数用init来标示。析构函数只适用于类类型。
Swift 通过自动引用计数(ARC)处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。

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

deinit {
     //执行析构过程
}

析构函数是在实例释放发生前一步被自动调用。不允许主动调用自己的析构函数。子类继承了父类的析构函数,并且在子类析构函数实现的最后,父类的析构函数被自动调用。即使子类没有提供自己的析构函数,父类的析构函数也总是被调用。

因为直到实例的析构函数被调用时,实例才会被释放,所以析构函数可以访问所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件的名称)。

析构这里就是官方的一些解释了 ,没有什么好解释的,语法就这种 ,在具体的业务中根据自己的需求具体使用吧。。

swift详解之七------------你真的了解构造过程吗构造过程和析构过程的更多相关文章

  1. three.js模拟实现太阳系行星体系功能

    这篇文章主要介绍了three.js模拟实现太阳系行星体系功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

  2. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. HTML5页面无缝闪开的问题及解决方案

    这篇文章主要介绍了HTML5页面无缝闪开方案,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

  5. ios – 在Swift中将输入字段字符串转换为Int

    所以我非常擅长制作APP广告Swift,我试图在文本字段中做一些非常简单的输入,取值,然后将它们用作Int进行某些计算.但是’vardistance’有些东西不正确它是导致错误的最后一行代码.它说致命错误:无法解开Optional.None解决方法在你的例子中,距离是一个Int?否则称为可选的Int..toInt()返回Int?因为从String到Int的转换可能失败.请参阅以下示例:

  6. 如何在iOS中检测文本(字符串)语言?

    例如,给定以下字符串:我想检测每个声明的字符串中使用的语言.让我们假设已实现函数的签名是:如果没有检测到语言,则返回可选字符串.因此,适当的结果将是:有一个简单的方法来实现它吗?

  7. xamarin – 崩溃在AccountStore.Create().保存(e.Account,“);

    在Xamarin.Forms示例TodoAwsAuth中https://developer.xamarin.com/guides/xamarin-forms/web-services/authentication/oauth/成功登录后,在aOnAuthenticationCompleted事件中,应用程序在尝试保存到Xamarin.Auth时崩溃错误说不能对钥匙串说期待着寻求帮助.解决方法看看你

  8. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

  9. xcode – 错误“线程1:断点2.1”

    我正在研究RESTAPI管理器.这是一个错误,我无法解决它.我得到的错误在下面突出显示.当我打电话给这个班级获取资源时:我评论的线打印:Thread1:breakpoint2.1我需要修复错误的建议.任何建议都非常感谢解决方法您可能在不注意的情况下意外设置了断点.单击并拖动代表断路器外部断点的蓝色刻度线以将其擦除.

  10. ios – 更改导航栏标题swift中的字符间距

    类型的值有人可以帮我这个或建议一种不同的方式来改变swift中导航栏标题中的字符间距吗?解决方法您无法直接设置属性字符串.你可以通过替换titleView来做一个技巧

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部