Method
-
方法
与计算型属性
的使用选取
1.优先考虑使用 computed property
(计算型属性) ,可以利用setter 、getter方法。
2.如果不需要用到setter
/getter
,并且需要处理数据库相关的复杂任务,那么建议使用方法Method
.
struct person {
var name: String
//很显然可以使用计算型属性来代替
func age() -> Int {
return 20
}
}
Tip: 一个返回单一值的方法,显然没有什么必要存在。
- self 介绍
一个结构体定义更像是一张蓝图,它的实例 Instance
作为一个真实的对象存在。Swift 编译器默认将self
作为一个隐含参数,传递给了method
。在结构体内部,允许使用 self
访问实例的属性或方法。
struct person {
var name: String
var age: Int
//这种传参方式没有什么意义
func description(name: String, age: Int) -> String {
return "\(name)'s age is \(age)"
}
}
Tip:换句话说,一个实例的方法如果访问的都是自身的属性,那么将他们作为参数传递以增加函数的复杂度是没有任何意义的。
构造函数
之前提到,Swift会根据结构体类型的属性来自动创建构造函数(包含所有属性,强制初始化),这仅仅是为了保证生成实例时保证所有属性是可以使用的状态。
struct Test{
var name: String
var age: Int
/*
//这个也被称为 简单构造函数
init() {
name = "Tom"
age = 18
}
*/
///自定义构造函数
func init(name: String, age: Int){
//这里需要注意
/*
//wrong 不要试图,因为编译器并不会知道哪个才是本地属性
name = name
age = age
*/
self.name = name
self.age = age
}
func description() -> String {
return name + String(age)
}
}
///注意,如果上面的构造函数处于注释状态,
///那么系统就会强制我们使用系统自动创建的构造函数来进行实例生成。
let test = Test(name: , age: )
///打开注释,才可以使用已经定义好的 init()构造函数,而自动生成的构造函数会变的不可用。
let testA = Test()
Tip:在你决定创建构造函数时,也就意味着放弃了使用系统为你自动创建好的构造函数。
- mutating methods (修改实例属性会遇到系统的保护屏障)
struct Test{
var name: String
var age: Int
init() {
name = "Tom"
age = 18
}
func description() -> String {
return name + String(age)
}
///修改age 的方法
func changeAge() {
age += 2
}
}
可以看到,我们创建了一个方法用来修改属性age。可以一旦编译,编译器会报如下错误:
Left side of mutating operator isn't mutable: 'self' is immutable
该错误的含义,仿佛是系统在告诉我们self
及其属性在结构体中默认是不可变的。当然,这是不包括构造函数的。显然,设计者并不希望我们随意改变内部环境。
- 思考一: 结构体作为系统提供的最基本数据类型,在某种程度上其类似于
OC
中NSObject
的地位。 - 思考二: 足够微小的基本类型,应该默认是固化的状态。
- 思考三: 内部不应该含有不直观的、隐藏的状态变更
- 思考四: 结构体是值类型valueType,copy-on-write
///遵照系统提示,我们会很容易注意到关键字 mutating operator ,修改如下
mutating func changeAge(){
age += 2
}
//由于结构体是值类型,当期作为参数传递时或者赋值时,默认会发生拷贝操作。那么如果内部存在能够影响属性值的方法,很有可能会导致原结构体和拷贝后的结构体会不一致。
mutating
操作中,Swift会隐含完成以下步骤:
1.被标记为'mutating'的函数,默认调用者将不再是固化的,也就是常量,应该是一个变量。
2.Swift会悄悄的把传入的隐含参数'self',标记为'inout'. 概念上类似于,传址。
- 类型方法(Type methods)
和类型属性(Type properties)一样,我们也可以自定义类型方法,在类型方法中我们可以访问全部的类型属性。其实这也是另一种单例形式~ 关键字static
Struct Person {
static func typeFunc(){
print("do whatever you want...")
}
}
//调用
Person.typeFunc()
- 为结构体添加扩展(仅限方法、构造函数、计算型属性)
同OC中类别Category
的概念一样,Swift中我们也可以很方便的通过扩展extension
为类型添加方法,计算型属性等。
extension Person {
static func typeFuncA() {
print("添加类型方法")
}
static var typeComputeProperty:String {
return "类型计算型属性"
}
func instanceFuncA() {
print("添加实例方法")
}
var instanceProperty:String {
return "实例计算型属性"
}
init() {
print("添加了构造函数")
}
}
上面的是会成功的情况,那么我们做一些错误尝试,来看一下编译器的反应。
///保留上面的扩展,新建一个扩展
extension Person {
/**
再次添加一个构造函数
*/
init() {
}
//编译器报错 Invalid redeclaration of 'init()'
/**
添加一个已有的方法
*/
func instanceFuncA() {
print("添加实例方法")
}
//编译器报错 note: 'emptyFunc()' previously declared
//那么 复写呢
override func instanceFuncA() {
print("添加实例方法")
}
//结果还是一样的警告,感兴趣的可以试一下复写属性等等
}
能够基本得出的结论,Swift 分类中是不能复写类型中已有的函数、属性的。这种限制也很合理,毕竟是不安全的行为。
还有一个有趣的现象,上面已经讲过当你为类型实现了构造函数时,就默认放弃了系统自动生成的构造函数的权利。不过,通过类别,我们可以做到让自定义的构造函数和自动构造函数同时可用。
struct Test{
var varA:String
var varB:String
}
//可用
Test(varA: "A",varB: "B")
extension Test{
init(){
varA = "A"
varB = "B"
}
}
//同时可用
Test()
Test(varA: "A",varB: "B")
在这里给自己留一个问题,为什么
Swift
不可以添加存储型属性?