自定义初始化方法
struct Student { var chinese: Int = 50 var math: Int = 50 var english: Int = 50 init() {} init(chinese: Int, math: Int, english: Int) { self.chinese = chinese self.math = math self.english = english } init(stringScore: String) { let cme = stringScore.characters.split(separator: ",") chinese = Int(atoi(String(cme.first!))) math = Int(atoi(String(cme[1]))) english = Int(atoi(String(cme.last!))) } } let student6 = Student() let student7 = Student(chinese: 90, math: 80, english: 70) let student8 = Student(stringScore: "70,80,90")
一旦我们自定义了初始化器,系统自动的初始化器就不起作用了,如果还需要使用到系统提供的初始化器,在我们自定义初始化器后就必须显式的定义出来。
- 定义其他方法
如果此时需要修改某个学生某科的成绩,该如何实现呢?可以定义下面的方法:
//更改某个学生某门学科的成绩 func changeChinese(num: Int, student: inout Student){ student.chinese += num } changeChinese(num: 20, student: &student7)
此时student7的语文成绩就由原来的70被修改到了90
但是此方法有两个明显的弊端:
1.学生的语文成绩chinese是Student结构体的内部成员,一个学生的某科成绩无需被Student的使用者了解。即我们只关心学生的语文成绩更改了多少,而不是关心学生语文成绩本身是多少。
2.更改一个学生的语文成绩本身就是和Student结构体内部成员计算相关的事情,我们更希望达到如下形如:
student7.changeChinese(num: 10)
因为只有学生本身清楚自己需要将语文成绩更改多少(更像是面向对象封装的思想)。很明显此时changeChinese(num:)方法是Student结构体内部的方法而不是外部的方法,所以我定义了一个修改某个学生数学成绩的内部方法用于和之前修改语文成绩的外部方法对比:
struct Studentes { var chinese: Int = 50 var math: Int = 50 var english: Int = 50 //修改数学成绩 mutating func changeMath(num: Int) { self.math += num } } var student7 = Studentes(chinese: 20, math: 30, english: 40) student7.changeMath(num: 10) print("student7:\(student7)")
运行结果:
尽管两者都能达到同样的效果,但是把修改结构体成员的方法定义在结构体内部显得更加合理同时满足面向对象封装的特点。以上两点就是我们为Student结构体内部添加changeMath(num:)的原因,他让我们把类型相关的计算表现的更加自然和统一,即自己的事情应该用自己的方法实现不应该被别人关心。
值得一提的是在结构体内部方法中如果修改了结构体的成员,那么该方法之前应该加入:mutating关键字。由于结构体是值类型,Swift规定不能直接在结构体的方法(初始化器除外)中修改成员。原因很简单,结构体作为值的一种表现类型怎么能提供改变自己值的方法呢,但是使用mutating我们便可以办到这点,当然这也是和类的不同点。
- 常见的结构体
Swift中很多的基础数据类型都是结构体类型,下面列举的是一些常用的结构体类型:
//表示数值类型的结构体: Int,Float,Double,CGFloat... //表示字符和字符串类型的结构体 Character,String... //位置和尺寸的结构体 CGPoint,CGSize... //集合类型结构体 Array,Set,Dictionary...
很多时候你不细心观察的话可能不会想到自己信手拈来的代码中居然藏了这么多结构体。另外有时候在使用类和结构体的时候会出现下面的情况
类方法:
// Person 类 class Person { var name: String = "jack" let life: Int = 1 } var s1 = Person() var s2 = s1 s2.name = "mike" s1
结构体方法:
// People 结构体数据结构 struct People { var name: String = "jack" let life: Int = 1 } var p1 = People() var p2 = p1 p2.name = "mike" p1
细心的同学可能已经发现了其中的诡异。变量s1、s2是Person类的实例,修改了s2的name属性,s1的name也会改变;而p1、p2作为People结构体的实例,修改了p1的name属性,p2的name并不会发生改变。这是为什么呢?
类能够改变是因为,类是引用类型,内部做浅拷贝处理,本质是指向同一个对象,自然可以改name; 结构体之前已经说过是值类型,内部做深拷贝处理,会重新生成一个对象,在复制时修改一个实例的数据并不影响副本的数据。
性能对比
其实本质是对比值类型和引用类型的性能,因此擂台上的选手就是结构体(struct)和类(class)。
测试1: 循环创建类和结构体
a.执行1亿次类创建
// 定义类 class StudentC{ var name:String init(name:String) { self.name = name } } // 统计时间 let date = Date() for i in 0...100_000_000{ let s = StudentC(name: "酷走天涯") } print(Date().timeIntervalSince(date))
运行三次结果:
19.3928480148315 20.9919492812921 20.7549253872943
b.执行10亿次结构体创建
// 定义结构体 struct StudentS{ var name:String init( name:String) { self.name = name } } let date = Date() for i in 0...1000_000_000{ let s = StudentS(name: "酷走天涯") } print(Date().timeIntervalSince(date))
运行三次结果:
9.99221454212455 10.9281648273917 10.7281881727434
我们上面的属性为基本数据类型,我们将属性改为对象测试一下速度
c.创建10_000_000个对象
class StudentC{ var date = NSDate() } for i in 0...10_000_000{ let s = StudentS() }
测试结果:
6.38509398698807 6.43649202585222 6.39519000053406
d.创建10_000_000个结构体实例
struct StudentS{ var date = NSDate() } for i in 0...10_000_000{ let s = StudentS() }
测试结果:
4.38509398698807 4.43649202585222 4.39519000053406
结论:创建结构体要比创建对象速度快