Swift 的构造器(一)

前言

      本文主要是Swift的构造器相关知识,另加少部分的OC中的init方法(还有少许Java相关的构造方法),通过两者的对比来加深对Swift构造器的理解,毕竟Swift是要淘汰的OC的,所以主要研究Swift也是必然。

指定构造器和便利构造器

        Swift构造器分为两类,一类是指定构造器(Designated Initializer),另一类是便利构造器(Convenience Initializer)。

      指定构造器

        指定构造器在一个类中必须至少有一个, 因为它是类的最主要构造器,没有之一,所有类的实例的初始化必然会调用指定构造器。以init开头,参数名、参数列表可以依照实际情况编写。当没有自定义指定构造器时,系统会自动生成一个不带参数的默认构造器,不带参数的默认构造器,意味着类的定义中,成员变量一定要赋上默认值。当有自定义指定构造器时,类的定义中,成员变量可以没有默认值,但是会在调用指定构造器时给其赋值,并且赋值时机要在调用父类的指定构造器之前(如果这个类有父类时)。举个例子,先定义一个基类。

```

class Person {

      // 成员变量 name 、 sex都是没有赋值

       var name:String

       var sex:String

       init(name:String, sex:String) {

            // 在指定构造器中给成员变量赋值

            self.name = name

            self.sex = sex

       }

}

```

  在这个基类Person中,成员变量 name和sex在定义时都没有赋值,只是指定数据类型,相关的赋值是在指定构造器init(name:String, sex:String)中完成的。如果把Person类改成下面这样

```

   // 这是个错误的定义

    class Person {

          var name:String

          var sex:String

    }

```

这个时候Xcode直接报错:

Swift 的构造器(一)_第1张图片

这里报错第一行显示是Person没有构造器,其他行显示name和sex没有初始值,其实本质来说,是因为成员变量在Swift中已经不是自动生成默认值,需要程序员自己指定。这里就需要提到一个不管是在Swift或者OC,还是Java或是其他面向对象的语言都有的概念:某个类的实例被创建,其成员变量一定要有值。在上面的错误定义中,因为Swift不再为成员变量自动生成默认值,如果程序员再不指定,这样在创建实例时成员变量会没有值,结果当然是直接就报错了。如果我们像下面这样稍微改动一下:

```

class Person {

      var name:String="李磊"

      var sex:String="男"

}

// 调用了Person自动生成的默认指定构造器(无参数)

var per = Person()

```

结果成功运行。在生成了per实例时,调用了自动生成的默认指定构造器,虽说是无参数,但是Person类在定义时,直接就给name和sex赋值了,所以这个per实例是成功被创建了。这里有一个不得不强调的一点,Swift的指定构造器本质是,确保本类的成员变量一定要被赋值,不是说一定要通过指定构造器来赋值。这个从上面改动的例子中可以看出(默认构造器并没有给成员变量赋值)。

       当某个类有父类时,在其指定构造器中必须调用父类的指定构造器,且在调用父类的指定构造器前,必须得确保这个类的成员变量必须得有值。为证明这些,再定义一个Man类继承自Person类

```

class Man:Person {

       var education:String="本科"

       // age此处并没有被赋值

       var age:Int

       init(name:String, sex:String, age:Int) {

            // 在调用父类的指定构造器之前,先给成员变量age赋值

            self.age=age

            // 调用父类的指定构造器

            super.init(nameStr: name, sexStr: sex)

        }

}

```

如果将上面的例子改成,在指定构造器中先调用父类的指定构造器,而后给成员变量age赋值,像下面一样

```

class Man:Person {

       var education:String="本科"

       // age此处并没有被赋值

       var age:Int

       init(name:String, sex:String, age:Int) {

              // 先调用父类的指定构造器

             super.init(nameStr: name, sexStr: sex)

              // 再给成员变量age赋值

              self.age=age

        }

}

```

结果Xcode报错。

因为age在定义的位置并没有被赋值,所以在调用父类的指定构造器时age无值。对此强调一点:指定构造器在调用父类的构造器前,一定要确保子类引入的成员变量要有值。

      便利构造器

      由convenience关键字修饰,是横向代理。横向代理的意思是,convenience构造器中必须调用同一个类中的其他一个构造器,这个构造器是指定构造器或者便利构造器都行,但是,如果是便利构造器的话,convenience构造器通过调用链(代理链)最终都得调用一个designated构造器。举个例子,将Person和Man类稍微改一下:

```

     class Person {

            var name:String

            var sex:String

            init(name:String) {

                self.name= name

                self.sex="男"

            }

            //指定构造器可以有多个,但是至少有一个

           init(nameStr:String, sexStr:String) {

                self.name= nameStr

                self.sex= sexStr

            }

           //定义便利构造器(使用convenience修饰)

          convenience init(nameString:String, sexString:String) {

                  //这里的便利构造器必须调用同类中的指定构造器,因为Person是基类,便利构造器无法沿着构造器的调用链调用到父类指定构造器,因为基类是没有父类的,Swift不是OC,OC中NSObject是所有对象的基类,而Swift是没有这种“终极”基类的。

                  self.init(nameStr: nameString, sexStr: sexString)

          }

          convenience init(speakWord:String) {

                  self.init(nameStr:"人类", sexStr:"")

                  print("Person--\(speakWord)")

          }

    }


class Man:Person{

       var education:String="本科"

       var age:Int= 10

       override init(name:String) {

             //子类的指定构造器中必须调用父类的指定构造器

             super.init(name: name)

       }

       override init(nameStr:String, sexStr:String) {

             super.init(nameStr: nameStr, sexStr: sexStr)

       }

       init(name:String, sex:String, age:Int) {

            self.age= 20

            super.init(nameStr: name, sexStr: sex)

        }

       //定义指定构造器与父类的便利构造器一样,这里不算重写

      convenience init(showStr:String, age:Int) {

             //这里调用的是从父类继承来的便利构造器

             self.init(speakWord:"hello!")

             self.age= 20

             print("Man---\(showStr)")

       }

}

```

在改过后的Man类中,就不得不提构造器的继承。之前说过构造器默认是不继承,但是在有些情况下,会继承。

1.子类没有定义任何的指定构造器, 那么就会自动从父类那里继承所有的指定构造器

2.如果子类中提供了所有父类指定构造器,不管是通过规则1(没有定义任何指定构造器)继承来的,还是自定义实现的,它将继承所有父类的便利构造器

在改后的Man类中,将Person类中的指定构造器都overrider了,满足第二种情况,所以父类的便利构造器都被继承了。对于上面的规则1,个人觉得其实还可以改的更通俗点儿,只要是子类没有定义指定构造器,子类会从父类那里继承所有的构造器(包括便利构造器),有兴趣的可以试下将Man类其他的构造器都删掉,只保留便利构造器,在编写便利构造器时,会发现Person类的所有构造方法都已被继承。还有一点可以看出,在Man类的便利构造器中,调用的是从父类继承来的便利构造器( 这一句代码:self.init(speakWord:"hello!") ),在该便利构造器中又调用了“init(nameStr:String, sexStr:String)”这个指定构造器,所以调用便利构造器,最终都会沿着调用链调用到一个指定构造器。

你可能感兴趣的:(Swift 的构造器(一))