在OC的基础上快速理解Swift的类与结构体

Swift中,类和结构体都是对数据和方法进行封装的常用做法!首先我们来看看他们的共同之处:

  1. 都可以有属性和方法;
  2. 都有构造器;
  3. 都支持附属脚本;
  4. 都支持扩展;
  5. 都支持协议。

然后我们来看看他们的不同之处:

  1. 类有继承;
  2. 结构体有一个自动生成的逐一初始化构造器;
  3. 在做赋值操作时,结构体总是被拷贝(Array有特殊处理);
  4. 结构体可以声明静态的属性和方法;
  5. 从设计模式的角度来分析,类的设计更侧重于对功能的封装,而结构体的设计更侧重于对数据的封装。(汽车与书架来区分类与结构体)

一、构造过程

1. 默认值

在OC 类和结构的成员属性会隐式的给出默认值,对象的默认值是nil,基本数据类型的默认值是0。但是在 Swift 中属性必须显示的设置默认值,Swift会在调用构造器之前调用默认值构造器对所有在设置了默认值的属性进行初始化。

当一个构造过程完成的时候,被构造的类或者结构体的实例的存储属性都会是有值的,否则编译错误!


1

2

3

4

5

6

7

8

9

10

11

    class A : SuperClass {

        var _a = 1      // 默认值 = Int(1)

        var _b: Int?    // 默认值 = nil

        var _c: Int     // 无默认值,必须在构造器中赋值

        var _d: Int!    // 默认值 = nil

        init() {

            _c = 1      // 在调用父类的构造器之前,必须给_c赋值,否则编译错误

            super.init()

        }

        ...

    }

2. 构造器

类似与OC的 -(instance)init 方法。和OC最大的区别是 OC 初始化完成后返回的是这个对象的指针,而Swift初始化完成后返回的是这个对象的引用。

根据构造器的实际构造过程可将构造器分为 便利构造器 和 指定构造器,但只有指定构造器才是真实的构造器,便利构造器只是为指定构造器添加一个便利的参数传入,或者增加一些附加操作。

以下几点在开发的时候需要注意:

  1. 类和结构体都需要在构造器完成对所有存储属性的初始化;
  2. 子类在调用父类的构造器之前必须确保本类声明的所有的存储属性都已经有值了;
  3. 便利构造器必须直接或者间接的调用本类的指定构造器。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

class A : SomeClass {

     var _a: Int

     var _b = 1_000

     // 指定构造器

     init() {

         self._a = 5     // 如果是这样声明的 ‘var _a: Int = 5‘ or ‘var _a = 5‘,那么init()构造器可以不写而直接调用

         super.init()

     }

     // 指定构造器

     init(a: Int) {      

         self._a = a     // 因为 _a 属性没有默认值,所以在调用 super.init() 前必须给 _a 赋值,否则编译错误!

         super.init() 

     }

     // 便利构造器

     convince init(a: Int, b: Int) {

         self.init(a: a + b)         // 直接调用指定构造器

     }

     // 便利构造器

     convince init(a: Int, b: Int, c: Int) {

         self.init(a: a, b: b + c)   // 间接调用指定构造器

     }   

     ...

 }

3. 析构器

和OC的 dealloc 很像,这里不多做解释了。


1

2

3

4

5

6

7

class A {

    ...

    deinit {    

        //...  析构器内部处理

    }

    ...

}

二、属性

OC中的类有属性,也有实例变量。但Swift中将类的实例变量和属性统一用属性来实现。

1. 存储属性

简单来说就是一个成员变量/常量。Swift可以为存储属性增加属性监视器来响应属性值被设置时的变化。


1

2

3

4

5

6

7

8

9

10

11

12

13

class SomeClass {

    let _a = 100        // 常量

    var _b: Int         // 没有默认值的变量

    var _c = 200        // 默认值=200的Int变量

    var _d: Int = 200 { // 默认值=200的Int变量,如果增加了属性监视器,必须显示的声明变量类型

        didSet {

            println("A 的 _c 属性 didSet = old:\(oldValue) -> \(self._c)")   // oldValue 表示曾经的值

        }

        willSet {

            println("A 的 _c 属性 willSet = \(self._c) -> new:\(newValue)")  // newValue 表示将会被设置的值

        }

    }

}

2. 计算属性

计算属性并不存储任何数据,他只是提供 set/get 的便利接口。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

class A {

    class var a: Int {  // 这是类的计算属性 (区别于下面类的实例的计算属性)

        return 1001

    }

    private(set) var _b = 100   // 外部可以访问,但不能修改

    private var _a = 100        // 私有变量,外部不能访问

    var a: Int {                // 只读计算属性

        return _a

    }

    var b: Int {                // 可读可写的计算属性

        get {

            retrun _a

        }

        set {

            _a = newValue       // newValue 表示的是输入的值

        }

    }

}

3. 延迟存储属性

相信大家都听说过延迟加载(懒加载),就是为了避免一些无谓的性能开销,在真正需要该数据的时候,才真正执行数据加载操作。 Swift可以使用关键字 lazy 来声明延迟存储属性,延迟存储属性在默认值构造器中不会被初始化,而是在第一次使用前进行初始化! 虽然没被初始化,但是编译器会认为他是有值的。

全局的常量或者变量都是延迟加载的, 包括结构体的静态属性也是延迟加载的。


1

2

3

4

let some = A()

class A {

    lazy var view = UIView(frame: CGRectZero) // 定义了一个延迟存储属性      ...

}

4. 静态属性

结构体可以使用关键字 static 来声明静态存储属性。(枚举也可以有) Swift的类不支持静态属性,也不支持静态临时变量。 这可以作为Swift中声明单例的一种实现方:


1

2

3

4

5

6

7

8

class Tools {

    class func sharedInstance() -> Tools {

        struct Static {

            static let sharedInstance = QNTools()

        }

        return Static.sharedInstance

    }

}

三、方法

Swift的类和结构体都可以定义自己的方法!(OC的结构体是没有方法的)

1. 参数

一个方法的参数有局部参数名称和外部参数名称,一样时写一个即可! Swift的方法的参数非常灵活,可以是值,可以是引用,可以是闭包,可以是元组... Swift的方法的返回值跟参数一样灵活

这里给出一个简单的加法方法来展示方法的参数:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

class A {

    // eg. 一个简单的相加方法

    // 完整版

    class func sum1(let a/*外部参数名称*/ aValue/*内部参数名称*/: Int, let b/*外部参数名称*/ bValue/*内部参数名称*/: Int) -> Int {

        return aValue + bValue

    }

    // 当函数参数的外部和内部参数相同的时候,可以只写一个,  此时第一个参数的名称在调用时是隐藏的

    class func sum2(a: Int, b: Int) -> Int {

        return a + b

    }

    // 使用 _ 符号,可以在调用的时候隐藏函数参数名称,第一个参数默认就是隐藏的

    class func sum3(a: Int, _ b: Int) -> Int {

        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明

        func sum4(a: Int, b: Int) -> Int {

            return a + b

        }

        return sum4(a, b)

    }

    // 可使用 let/var 关键字来声明参数是作为常量还是变量被传入,(如果是常量,可以不用显示的写 let)

    class func sum4(let a: Int, _ b: Int) -> Int {

        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明

        func sum4(a: Int, b: Int) -> Int {

            return a + b

        }

        return sum4(a, b)

    }

    // 可使用 let/var 关键字来声明参数是作为常量还是变量被传入,(如果是常量,可以不用显示的写 let)

    class func sum5(let a: Int, let _ b: Int) -> Int {

        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明

        return a + b

    }

    class func sum6(var a: Int, var _ b: Int) -> Int {

        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明

        a++

        b++

        return a + b

    }

    // 可使用 inout 关键字,传引用

    class func add(inout value: Int) {

        value++

    }

}


1

2

3

4

5

6

7

8

A.sum1(a: 1, b: 2)          // result: 3

A.sum2(1, b: 2)             // result: 3

A.sum3(1, 2)                // result: 3

var aTemp: Int = 1001       // aTemp = 1001

A.add(&aTemp)

aTemp                       // aTemp = 1002

A.sum5(1, 2)               // result: 3

A.sum6(1, 2)               // result: 5

2. 实例方法

类或者结构体的实例所拥有的方法!


1

2

3

4

5

6

7

8

9

10

11

12

class A {

    private(set) var a: Int = 100

    func reset() -> Void {

        self.a = 100

    }

}

struct S {

    var a: Int = 100

    mutating func reset() -> Void { // 注意: 在结构体和枚举的实例方法中如果需要修改属性,则需要增加 mutating 字段

        self.a = 100

    }

}

3. 类方法

Swift 中类的本身也是一个实例。他没有存储属性、但拥有计算属性和方法!

参考 “1.参数” 中的示例

4. 静态方法

结构体可以使用关键字 static 来声明静态方法。


1

2

3

4

5

struct S {

    static func name() -> String {

        return "Liuyu"

    }

}

四、附属脚本

Swift 提供了附属脚本的语法来简化类似查询的行为。如访问一个数组的第n个元素,array[n], 访问一个字典的值 dictionary[“key”],这些都可以通过附属脚本来实现。 这里给出一个通过字符串类型的索引来查询数组中的元素的例子。


1

2

3

4

5

6

7

8

9

10

11

12

// 扩展一个通过字符串类型的位置来访问数据的附属脚本

extension Array {

    subscript(index: String) -> T? {

        if let iIndex = index.toInt() {

            return self[iIndex]

        }

        return nil

    }

}

let array = ["Liu0""Liu1""Liu2"]

array[1]    // result : Liu1

array["1"]! // result : Liu1

五、继承

和OC一样,Swift类可以通过继承来获取父类的属性和方法(有限制)。 Swift的类的在继承上增加了很多安全措施


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

class SomeSuperClass {

    func someFunc1() {

        ...

    }

    // 定义不能被子类重写的方法,需要加上 final 关键字

    final func someFunc2() {

        ...

    

}

class SomeClass : SomeSuperClass {

    // 重载父类的方法,需要加上 override 关键字

    override func someFunc1() {

        ...

        super.someFunc1()

    }

    // 不能被子类重写的方法

    override func someFunc2() {     // Error

        ...

        super.someFunc2()

    }

}

六、扩展

扩展就是向一个已有的类、结构体或枚举类型添加新功能。 这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)。 扩展和OC中的类别(categories)类似,但又有很多细微的区别:

  1. 扩展没有名字,一旦扩展就会应用到整个工程(模块)
  2. 在扩展中如果要重载现有的方法,需加上override关键字 (不建议修改现有的方法)
  3. 可定义新的嵌套类型

这里给出一个数学项目中计算距离时的一个扩展


1

2

3

4

5

6

7

8

9

10

11

typealias Distance = Double

extension Distance {

    var km: Double { return self/1_000.0 }

    var m : Double { return self }

    var cm: Double { return self*100.0 }

    var mm: Double { return self*1_000.0 }

}

let distance = Distance(1000)   // 1000m的距离

distance.km     // result : 1.0     (km)

distance.m      // result : 1000.0  (m)

distance.cm     // result : 100.0   (cm)

 

时间: 2024-10-13 06:06:45

在OC的基础上快速理解Swift的类与结构体的相关文章

[转]站在OC的基础上快速理解Swift的类与结构体

(阅读此文章前,您已经有一定的Object-C语法基础了!) 2014年,Apple推出了Swift,最近开始应用到实际的项目中. 首先我发现在编写Swift代码的时候,经常会遇到Xcode不能提示,卡顿,直接闪退等问题,尤其是在Swift和OC混编时.(不知道其他开发者是否也有这样的经历,但是我相信这样的问题,很快会得到解决) 然后感觉Swift并不像网上很多朋友说的那样简单.有很多细节问题是值得注意的,甚至有很多思路是颠覆了传统的开发语言的!又有很多特性是结合了其他编程语言的优点! Swif

Swift 的类、结构体、枚举等的构造过程Initialization(上)

构造过程是为了使用某个类.结构体或枚举类型的实例而进行的准备过程.这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务. 构造过程是通过定义构造器(Initializers)来实现的,这些构造器可以看做是用来创建特定类型实例的特殊方法.与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化. 类实例也可以通过定义析构器(deinitializer)在类实例释放之前执行特定的清除工作.想了解更多关于

Swift学习(类和结构体)

类和结构体: 相同点: 1.都可以定义属性 2.都可以定义方法,这一点是Swift根C,OC的明显区别之一 3.他们都可以定义构造器(初始化方法) 4.都可以遵循协议 5.他们都可以被扩展 不同点: 1.类可以被继承,而结构体不可以 2.类可以被类型推断 3.类可以通过 析构(dealloc) 释放内存 4.类是 引用类型, 结构体是 值类型 使用 class 和 struct 来分别表示类和结构体 定义 类 class Car { var name : String? func printNa

Swift 的类、结构体、枚举等的构造过程Initialization(下)

类的继承和构造过程 类里面的全部存储型属性--包含全部继承自父类的属性--都必须在构造过程中设置初始值. Swift 提供了两种类型的类构造器来确保全部类实例中存储型属性都能获得初始值,它们各自是指定构造器和便利构造器. 指定构造器和便利构造器 指定构造器是类中最基本的构造器.一个指定构造器将初始化类中提供的全部属性,并依据父类链往上调用父类的构造器来实现父类的初始化. 每个类都必须拥有至少一个指定构造器.在某些情况下,很多类通过继承了父类中的指定构造器而满足了这个条件.详细内容请參考兴许章节自

swift 类 与 结构体

这两天突然有人问我  swift里面 类和 结构体  有什么区别? 说实在的本人目前不太看好swift,相信很多人也是,oc 都 很成熟了. 本人目前不打算深入了解swift的原因swift  语言 虽然也已经出来很久了,但是总感觉还有许多东西 不如oc稳定.  每个 版本的 swift  都会有比较大的变动. 所以干脆先等等,等到工作不忙 swift也稳定了,然后再看也不迟. 但是 有些里面已经稳定了的东西可以先作为了解内容.  今天就说下类  和  结构体. http://c.bianche

swift基础学习(04)[闭包、函数、枚举、类和结构体]

//闭包表达式:闭包表达式是一种利用简洁语法构建内联闭包的方式 //sort方法:排序功能,排序后的原来的不会被修改 //:从大到小排序 let names = ["d","ds","wa","ad"] func sortNames(sName1:String,sName2:String) -> Bool { return sName1 > sName2 } var result = names.sort(sort

Swift:什么时候使用结构体和类

发布于 2015 年 8 月 14 日 世界上对swift持续不断的讨论话题中有一个就是什么时候使用结构体什么时候使用类.我想我今天要贡献一些自己的想法. 值 VS 引用 答案其实很简单:当你需要值语义(所谓值语义是一个对象被系统标准的复制方式复制后,与被复制的对象之间毫无关系,可以彼此独立改变互不影响)的时候使用结构体,当你需要引用语义(所谓值语义是一个对象被系统标准的复制方式复制后,与被复制的对象之间毫无关系,可以彼此独立改变互不影响)的时候使用类.就是那样! 欢迎下周再来... 等等! 怎

Swift的闭包,枚举,类和结构体

闭包(Closures) 使用过其它语言的应该对代码块并不陌生,Swift中的闭包与C,OC中的Block相似. 表示自包括的函数代码块.能够在代码中传递和使用. 而且能够捕获和存储上下文的变量以及常量值,Swift会为你进行捕获相关的内存操作. 上一篇文章提到的函数.也是一种特殊的闭包.详细在: 全局函数是有名字可是不会捕获不论什么值的闭包. 嵌套函数是有名字且能够捕获域内值的闭包. 闭包表达式是利用轻量级语法写的能够捕获上下文值的匿名闭包. 基本的语法 表达式的一般语法以及简化过程 上面罗列

Swift学习之类和结构体的创建

随着一步步的学习,学习到的新知识越来越多了,不管是新的还是旧的,都禁不住时间的堆积,再熟悉的知识点时间久了都会渐渐的忘记,也许这就是人们生活中一种潜在的惰性吧,看似非常熟悉的东西,等到真正要用的时候,却拿不出手来,总是似懂非懂的,所以无论知识点多么多,过的时间多久,我们都要经常拿出来练习下,以免忘记.这些对于我们来说都是非常重要的,有的人会认为我记性非常好,写一遍都可以记住,但是真的是这样吗,所话说的好,好记性不如烂笔头,说的就是那些好吃懒做的人,所以我希望大家都可以多动手,经常练习我们所学过的