开篇
声明一个类、结构体或枚举,就像绘制了一副草图,即使描绘得再栩栩如生,也仅跃于纸上罢了。
举例来说,描述一辆汽车:车型、车身颜色、出厂日期等等。为此声明一个 Car 的类:
class Car{
var name:String /**> 车辆型号 */
var color:String /**> 车身颜色 */
var date:NSDate /**> 出厂日期 */
// 当然还有其他属性
}
此时手上只有Car的设计图,这并不意味着你已经拥有一辆保时捷或法拉利可以去兜风了;所以喽,还是将设计稿图交给工厂,由他们按照设计图制造一辆货真价实的小汽车交付给你。ps:制造出来的车在编程中叫实例(顾名思义:实际的例子)。
等等,工厂拒绝了!拒绝理由:车型没有指明,喷什么颜色呢?
修改如下:
class Car{
var name:String = "保时捷911" /**> 车辆型号 */
var color:String = "红色" /**> 车身颜色 */
var date:NSDate = NSDate() /**> 出厂日期 */
// 当然还有其他属性
}
ok!默认车型“保时捷”、车身颜色“红色”,出厂日期为加工当天。
独乐乐不如众乐乐,于是大手一挥,再加工一打!
厂家:还是全红色的保时捷911?
当初想得过于简单,设计稿图参数全给了默认值,失策失策!
修改如下:
class Car{
var name:String /**> 车辆型号 */
var color:String /**> 车身颜色 */
var date:NSDate /**> 出厂日期 */
// 当然还有其他属性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
至此,告诉厂家:第一辆要蓝色的保时捷911,第二辆要红色的保时捷Cayenne,第三辆...
几种类型的Initialization
枚举:
enum Direction{
case East, South, West, North
init(symbol:Character){
switch symbol{
case "E":
self = .East
case "S":
self = .South
case "W":
self = .West
case "N":
self = .North
default:
self = .East
}
}
}
let dir = Direction(symbol: "S") // .South
结构体1:
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
let temp = Fahrenheit()
结构体2:
struct User {
var username:String
var password:String
}
// 尽管没有声明初始化方法 但是对于结构体默认是有memberwise initialization
let user = User(username: "test", password: "123456")
类:
// 类初始化
class Car{
var name:String
var color:String
var age:Int
init(name:String,color:String,age:Int){
self.name = name
self.color = color
self.age = age
}
}
何为Initialization
根据设计稿图制造出车辆实例,在车辆出厂使用前,必须确认车辆车型和为车身喷漆,这是一个 Initialization 过程,保证车辆实例使用前完成所有的准备工作。
官方文档对此的描述是:
This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.
关键词:stored property存储属性,换句话说对于computed property计算属性是排除在外的。
再来说说 Swift 中 Initialization 的声明形式,使用init
修饰符即可:
init() {
// perform some initialization here
}
默认值
倘若你偏爱红色的保时捷,那么你可以一开始就为实例属性color
设定默认属性为“红色”
class Car{
// ...
var color:String = "红色" /**> 车身颜色 */
// ...
}
自定义
倘若你天马星空,那么你就应该声明一个自定义的构造方法,传入配色和车型来实例化车辆。
class Car{
var name:String /**> 车辆型号 */
var color:String /**> 车身颜色 */
var date:NSDate /**> 出厂日期 */
// 当然还有其他属性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
局部和外部参数名
当然就像函数(function)和方法(method)一样,构造方法也是可以拥有local name 和 external name。
Swift 自动为每一个参数提供 external parameter 名字,换句话说你丫别提供了!!
来自官方文档的例子:
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red //这里的red green blue 是局部参数名
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
// 这里的red green blue 是外部参数名字
// 这个是很有必要的,否则调用者怎么知道我传入的1.0 0.0 1.0 到底赋值给哪个参数了呢!!!
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
注意到red
、blue
和green
即使local parameters,能够在init
中使用;又可以在构造方法调用时作为external parameters 呈现
总有些“捣蛋鬼”就是想反其道而行:不要外部标签可以吗?? 好吧,答案自然是ok。使用** _ **下划线占据external parameter name的位置,告知编译器在调用构造方法时忽略外部参数名:
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
实例变量是可选参数类型
说说车辆的牌照,车辆刚制造出来时,对于车牌没有强制要求,一个月后上牌就OK。所以喽,我们可以为Car设定一个可选类型的车牌实例变量:
class Car{
var licensePlate:String? /**> 因为车牌一开始可以是没有的
var name:String /**> 车辆型号 */
var color:String /**> 车身颜色 */
var date:NSDate /**> 出厂日期 */
// 当然还有其他属性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
let car = Car(name:"保时捷911",color:"红色")
// 摇到号了
car.licensePlate = "浙A123456"
常量属性
让我们在“计较”点,车辆车型选定后,之后是绝无修改的可能,要知道改装车辆是违法的,所以喽,将name
声明为常量是一个明智的选择。
class Car{
var licensePlate:String? /**> 因为车牌一开始可以是没有的
let name:String /**> 车辆型号 */
var color:String /**> 车身颜色 */
var date:NSDate /**> 出厂日期 */
// 当然还有其他属性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
默认的 Initializers
Swift 为以下对象提供默认构造方法:
- Structure(结构体)
- 所有实例属性均设置了默认值的Class(类)
以官方文档为例:
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
对于类来说,我们并未写一个init
方法,而是给定每一个实例属性默认值。
结构体有点特殊,无须为实例属性设定默认值,先看看结构体的声明:
struct StructTest{
// memberwise 逐个成员初始化呗
var para1:String
var para2:String
var para3:String
var para4:String
}
注意我们并未给任何一个参数设定初始值,但实际上结构体已经就自定 memberwise initializer了(memberwise:逐个成员的意思),即生成了init(para1:,para2:,para3:,para4:)
构造方法了!
值类型中的 Initializer Delegation
对于值类型(结构体和枚举)来说,是没有继承的,这意味着只允许构造方法之间的互相调用,即使用self.init
,而没有super.init
一说。
再次提醒,构造方法的作用就是确保实例在使用前,其所有实例属性都设定了初始值!
举例:
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
// 1
init() {}
// 2
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
// 3
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
// 至于这里调用self.init() 无非就是不想在写self.origin = origin和self.size = size
// 由于这里初始化代码简单,所以作用感觉不出,但是某些情况下确实可以节省代码量
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
1、2 和 3 都是构造方法,其中1中,由于我们为结构体中的所有成员都给定了默认值,因为init(){}
方法中什么都没写,但是倘若我们未给结构体成员默认值,那么init(){}
报错Return from initializer without all stored properties
很好理解,构造方法的职责就是为所有实例变量在使用前设定初始值!假若你没有那么做,自然报错喽。
网友评论