iOS Principle:NamedTypes(上)

简介: iOS Principle:NamedTypes(上)

方便记忆:


  • Named types 的四种类型:enum、struct、class、protocol
  • enum:值类型,一组有限种可能性的相关值提供的通用类型
  • struct:值类型,一系列具有相同类型或不同类型的数据构成的数据集合
  • class:引用类型,类可继承,允许类型转换,有引用计数,析构方法释放资源
  • protocol:引用类型,协议中没有属性和方法的实现,可以当做类型来使用


前言


在开发过程中常常需要用到系统提供的基础类型之外的的类型,Swift 作为一款成长中的语言,自然敏锐的注意到这一点,所以 Swift 允许我们根据自己的需要构建属于自己的类型系统,以便于更加灵活和方便的开发程序并其称之为named types。

Named types 的四种类型:

  • enum
  • struct
  • class
  • protocol

相比于 Objective-C 中的这三者,Swift 将 enum 和 struct 变得更加灵活且强大,并且赋予了他们很多和class 相似的属性实现更加丰富多彩的功能。

本文重点介绍在 Swift 中 enum 和 struct 的定义和新特性以及两者与 class 之间的异同。


枚举(enum)


Swift 中的枚举的定义与C家族有些许不同,Swift 中枚举定义是“为一组有限种可能性的相关值提供的通用类型”,而在 C/C++/C# 中,“枚举是一个被命名的整型常数的集合”。

使用枚举可以类型安全并且有提示性地操作这些值。与结构体、类相似,使用关键词enum来定义枚举,并在一对大括号内定义具体内容包括使用case关键字列举成员。

就像下面一样:

enum StudentType {
    case pupils
    case middleSchoolStudent
    case collegeStudent
}

上面的代码可以读作:如果存在一个StudentType的实例,他要么是pupils (小学生)、要么是middleSchoolStudent(中学生)、要么是collegeStudent(大学生)。

和C、objective-c中枚举的不同,Swift 中的枚举成员在被创建时不会分配一个默认的整数值。而且不强制要求给枚举中的每一个成员都提供值。

如果一个值(所谓“原始值”)要被提供给每一个枚举成员,那么这个值可以是字符串、字符、任意的整数值,或者是浮点类型。

Swift中定义的枚举只需要帮助我们表明不同的情况就够了,他的成员可以没有值,也可以有其他类型的值,不像 OC 中枚举值只局限于整数类型,缺点就是你再回去写 OC 时可能会不习惯。

枚举中有两个很容易混淆的概念:原始值(raw value)、关联值(associated value),两个词听起来比较模糊,下面简单介绍一下:

  • 枚举的原始值(raw value)

枚举成员可以用相同类型的默认值预先填充,这样的值我们称为原始值(raw value)。

//枚举的原始值(raw value)
enum StudentType: Int{
    case pupil = 10
    case middleSchoolStudent = 15
    case collegeStudents = 20
}

上面的StudentType中三个成员分别被Int类型的10 、15、 20填充表示不同阶段学生的年龄。

注意:Int修饰的是StudentType成员原始值的类型而不是StudentType的类型,StudentType类型从定义开始就是一个全新的枚举类型。

//  常量student1值是 10
let student1 = StudentType.pupils.rawValue
print("student1:\(student1)")
//  变量student2值是 15
let student2 = StudentType.middleSchoolStudent.rawValue
print("student2:\(student2)")
//  使用成员rawValue属性创建一个`StudentType`枚举的新实例
let student3 = StudentType.init(rawValue: 15)
//  student3的值是 Optional<senson>.Type
print("student3:\(type(of: student3))")
//  student4的值是nil,因为并不能通过整数30得到一个StudentType实例的值
let student4 = StudentType.init(rawValue: 30)
print("student4:\(student4)")


image.png


使用原始值初始化器这种方式初始化创建得到StudentType的实例student4是一个StudentType的可选类型,因为并不是给定一个年龄就能找到对应的学生类型,比如在StudentType中给定年龄为30就找不到对应的学生类型(很可能30岁的人已经是博士了)

总之,原始值是为枚举的成员们绑定了一组类型必须相同值不同的固定的值(可能是整型,浮点型,字符类型等等)。这样很好解释为什么提供原始值的时候用的是等号。

  • 枚举的关联值(associated value)

关联值和原始值不同,关联值更像是为枚举的成员们绑定了一组类型,不同的成员可以是不同的类型(提供关联值时用的是括号)。例如下面的代码:

//枚举的关联值(associated value)
//定义一个表示学生类型的枚举类型 StudentType,他有三个成员分别是pupils、middleSchoolStudent、collegeStudents
enum StudentTypeAssociated {
    case pupils(String)
    case middleSchoolStudent(Int, String)
    case collegeStudents(Int, String)
}

这里我们并没有为StudentType的成员提供具体的值,而是为他们绑定了不同的类型,分别是pupil绑定String类型、middleSchoolStudent和collegeStudents绑定(Int, String)元祖类型。接下来就可以创建不同StudentType枚举实例并为对应的成员赋值了。

//student1 是一个StudentType类型的常量,其值为pupil(小学生),特征是"have fun"(总是在玩耍)
let student1 = StudentTypeAssociated.pupils("have fun")
print("student1:\(student1)")
//student2 是一个StudentType类型的常量,其值为middleSchoolStudent(中学生),特征是 7, "always study"(一周7天总是在学习)
let student2 = StudentTypeAssociated.middleSchoolStudent(7, "always study")
print("student2:\(student2)")
//student3 是一个StudentType类型的常量,其值为collegeStudent(大学生),特征是 7, "always LOL"(一周7天总是在撸啊撸)
let student3 = StudentTypeAssociated.middleSchoolStudent(7, "always LOL")
print("student3:\(student3)")

这个时候如果需要判断某个StudentType实例的具体的值就需要这样做了:

switch student3 {
    case .pupils(let things):
        print("is a pupil and \(things)")
    case .middleSchoolStudent(let day, let things):
        print("is a middleSchoolStudent and \(day) days \(things)")
    case .collegeStudents(let day, let things):
        print("is a collegeStudent and \(day) days \(things)")
}



image.png


控制台输出:is a collegeStudent and 7 days always LOL,看到这你可能会想,是否可以为一个枚举成员提供原始值并且绑定类型呢,答案是不能的!因为首先给成员提供了固定的原始值,那他以后就不能改变了;而为成员提供关联值(绑定类型)就是为了创建枚举实例的时候赋值。

  • 递归枚举

递归枚举是拥有另一个枚举作为枚举成员关联值的枚举。

关于递归枚举我们可以拆封成两个概念来看:递归 + 枚举。递归是指在程序运行中函数(或方法)直接或间接调用自己的这样一种方式,其特点为重复有限个步骤、格式较为简单。

下面是一个经典的通过递归算法求解n!(阶乘)的函数。

func factorial(n: Int)->Int {
    if n > 0 {
        return n * factorial(n: n - 1)
    } else {
        return 1
    }
}
    //1 * 2 * 3 * 4 * 5 * 6 = 720
    let sum = factorial(n: 6)

函数factorial (n: int)-> Int在执行过程中很明显的调用了自身。结合枚举的概念我们这里可以简单的理解为递归枚举类似上面将枚举值本身传入给成员去判断的情况。

可以看出Swift中枚举变得更加灵活和复杂,有递归枚举的概念,还有很多和类类似的特性,比如:计算属性用来提供关于枚举当前值的额外信息;实例方法提供与枚举表示值相关的功能;定义初始化器来初始化成员值;而且能够遵循协议来提供标准功能等等。


结构体(struct)


结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。结构体是一种值类型的数据结构,在Swift中常常使用结构体封装一些属性甚至是方法来组成新的复杂类型,目的是简化运算。

定义好的结构体存在一个自动生成的成员初始化器,使用它来初始化结构体实例的成员属性。

struct Student {
    var chinese: Int
    var math: Int
    var english: Int
}

看到木有,定义结构体类型时其成员可以没有初始值,要是类那个坑货早报错了...(提醒这个类没有被初始化)

  • 结构体实例的创建

创建结构体和类的实例的语法非常相似,结构体和类两者都能使用初始化器语法来生成新的实例。

最简单的语法是在类或结构体名字后面接一个空的圆括号,例如:

let student1 = Student()

这样就创建了一个新的类或者结构体的实例,任何成员都被初始化为它们的默认值(前提是成员均有默认值)。


image.png


但是如果定义结构体时没有设置初始值,像上面直接()的写法编译器会报错(出来混迟早是要给初始值的)

//使用Student类型的结构体创建Student类型的实例(变量或常量)并初始化三个成员(这个学生的成绩会不会太好了点)
let student2 = Student(chinese: 90, math: 80, english: 70)

所有的结构体都有一个自动生成的成员初始化器,你可以使用它来初始化新结构体实例的成员就像上面一样(前提是没有自定义的初始化器)。如果我们在定义Student时为他的成员赋上初值,那么下面的代码是编译通过的:

struct Students {
    var chinese: Int = 50
    var math: Int = 50
    var english: Int = 50
}
let student3 = Students()
print("student3:\(student3)")


image.png


总之,定义结构体类型时其成员可以没有初始值,但是创建结构体实例时该实例的成员必须有初值。

  • 自定义的初始化器

当我们想要使用自己的方式去初始化创建一个Student类型的实例时,系统提供的成员初始化器可能就不够用了。例如,我们希望通过如下方式创建实例时,就需要自定义初始化方法了:

let student5 = Student(stringScore: "70,80,90")


目录
相关文章
|
iOS开发
iOS Principle:CGAffineTransform
iOS Principle:CGAffineTransform
142 0
iOS Principle:CGAffineTransform
|
安全 Unix API
iOS Principle:CALayer(下)
iOS Principle:CALayer(下)
138 0
iOS Principle:CALayer(下)
|
iOS开发
iOS Principle:CALayer(中)
iOS Principle:CALayer(中)
120 0
iOS Principle:CALayer(中)
|
API C语言 iOS开发
iOS Principle:CALayer(上)
iOS Principle:CALayer(上)
142 0
iOS Principle:CALayer(上)
|
存储 缓存 iOS开发
iOS Principle:weak
iOS Principle:weak
119 0
iOS Principle:weak
|
存储 iOS开发
iOS Principle:Notification(下)
iOS Principle:Notification(下)
88 0
iOS Principle:Notification(下)
|
设计模式 iOS开发
iOS Principle:Notification(上)
iOS Principle:Notification(上)
101 0
iOS Principle:Notification(上)
|
Web App开发 JSON 移动开发
iOS Principle:ReactNative(下)
iOS Principle:ReactNative(下)
154 0
iOS Principle:ReactNative(下)
|
移动开发 前端开发 JavaScript
iOS Principle:ReactNative(中)
iOS Principle:ReactNative(中)
99 0
iOS Principle:ReactNative(中)
|
移动开发 开发框架 自然语言处理
iOS Principle:ReactNative(上)
iOS Principle:ReactNative(上)
120 0