Swift 学习笔记 (初始化)
初始化是为类 结构体 或者枚举准备实例的过程。这个过程需要给实例里的每一个存储属性设置一个初始值并且在新实例可以使用之前执行任何其它所必需的配置或初始化。
初始化器
初始化器在创建特定类型的实例时被调用。在这个简单的形式中,初始化器就像一个没有形式参数的实例方法,使用 init 关键字来写:
init() {
// perform some initialization here
}
代码示例
struct Fahrenheit {
var temperature:Double init() {
temperature = 2.0;
}
} var f = Fahrenheit()
f.temperature //
默认的属性值
struct Fahrenheit { var temperature = 32.0 }
初始化形式参数
struct Celsius {
var temperatureInCelsius:Double init(fromFahrenheit fahrenheit:Double) {
temperatureInCelsius = fahrenheit
}
init(fromKelvin kelvin:Double) {
temperatureInCelsius = kelvin - 273.15
}
} let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) let freezingPointOfWater = Celsius(fromKelvin: 273.15)
可选属性类型
如果你的自定义类型有一个逻辑上是允许无值的存储属性。声明属性为可选类型。可选类型的属性自动地初始化为nil,表示该属性在初始化期间故意的设为还没有值。
class SurveyQuestion {
var text:String
var response:String?
init(text:String) {
self.text = text
}
func ask() {
print(text)
}
} let cheeseQuestion = SurveyQuestion(text: "do you like cheese?")
cheeseQuestion.ask()
cheeseQuestion.response = "Yes,I do like cheese."
对调查问题的回答直到被问的时候才能知道,所以 response 属性被声明为 String? 类型,或者是“可选 Stirng ”。当新的 SurveyQuestion 实例被初始化的时候,它会自动分配一个为 nil 的默认值,意味着“还没有字符串”。
在初始化中分配常量属性
你可以修改上面的SurveyQuestion例子,给text使用常量属性而不是变量属性来表示问题,来明确一旦SurveyQuestion的实例被创建,那个问题将不会被改变。尽管现在的text属性是一个常量。但他依然可以在类的初始化器里设置。
class SurveyQuestion {
let text:String
var response:String?
init(text:String) {
self.text = text
}
func ask() {
print(text)
}
} let cheeseQuestion = SurveyQuestion(text: "how about beets?")
cheeseQuestion.ask()
cheeseQuestion.response = "I also like beets."
默认初始化器
Swift为所有媒体公初始化器的结构体或者类提供了一个默认的初始化器来给所有的属性提供了默认值。这个磨人的初始化器只是简单的创建了一个所有属性都有默认值的新实例。
class shoppingListItem {
var name:String?
var quantity =
var purchased = false
}
var item = shoppingListItem()
结构体类型的成员初始化器
如果结构体类型中没有定义任何自定义初始化器,它会自动获得一个成员初始化器。不同于默认初始化器,结构体会接收成员初始化器即使它的存储属性有默认值。
这个成员初始化器是一个快速初始化新结构体实例成员属性的方式。新实例的属性初始值可以通过名称传递到成员初始化器里
struct Size {
var width = 0.0,height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
值类型的初始化器委托
初始化器可以调用其它初始化器来执行部分实例的初始化。这个过程叫做初始化器委托。避免了多个初始化器里冗余代码。
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()
init() { }
init(origin:Point,size:Size) {
self.origin = origin
self.size = size
}
init(center:Point,size:Size) {
let originX = center.x - (size.width / )
let originY = center.y - (size.height / ) self.init(origin:Point(x: originX, y: originY),size:size)
}
} let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
类的继承和初始化
class Avatar {
var name:String
var life = { didSet {
if life > {
life =
}
if life <= {
self.isAlive = false
}
}
}
var isAlive:Bool = true
var description:String {
return "I am avatar \(name)"
}
init(name:String) {
self.name = name
}
//便利构造函数
convenience init(firstName:String,secondName:String) {
self.init(name:firstName + secondName)
}
func beAttacked(attack:Int) {
life -= attack
if life <= {
isAlive = false
}
}
} class User:Avatar {
var score =
var level =
var group:String
//重写属性
override var description: String {
return "I am user \(name)"
} func getScore(score:Int) {
self.score += score
if score > level * {
level +=
}
}
//先把子类的所有初始化完成 之后才能调用父类的构造方法
init(name: String,group:String = "") {
//第一阶段 构造实例
self.group = group;
super.init(name: name)
//第二阶段 逻辑判断 self的使用必须在构造完成以后
if group == "" {
self.getScore(score: -)
}
}
//便利构造函数 构造函数中调用了自己的构造函数
convenience init(group: String = "") {
let name = User.generateUserName()
self.init(name:name,group:group)
}
static func generateUserName() ->String {
return "Player" + String(arc4random()%)
}
} class Magician:User {
var magic =
//重写属性
override var description: String {
return "I am magician \(name)"
} func heal(user:User) {
user.life +=
} //构造函数也可以重写
override init(name: String, group: String) {
let defalutGroups = ["Gryffindor","Hufflepuff","Ravenclaw","Slytherin"]
for theGroup in defalutGroups {
if group == theGroup {
super.init(name: name, group: group)
return
}
}
let group = defalutGroups[Int(arc4random()%)]
super.init(name: name, group: group)
}
} final class Warrior:User {
var weapon:String
//重写属性
override var description: String {
return "I am warrior \(name)"
}
//重写方法
override func beAttacked(attack: Int) {
self.life -= attack /
}
//构造函数也可以有默认值
init(name: String, group: String,weapon:String = "Sword") {
self.weapon = weapon super.init(name: name, group: group)
}
} class Monster:Avatar {
func attack(user:User,amount:Int){
user.beAttacked(attack: amount)
}
} final class Zombie:Monster {
var type = "default"
} let user = User(name: "tian", group: "lh")
//多态 一个实例同时属于多个类 就是多态的一种形式 一个方法有不同的响应也是多态的一种形式。在OC中体现为:不同对象对同一消息的不同响应.子类可以重写父类的方法 //因为构造函数有默认值 所以在构造的时候会有两个构造函数
let warrior = Warrior(name: "tian", group: "lh")
warrior.weapon //有一个默认值
构造方法的继承
如果子类实现了父类所有的指定构造函数,难么子类会自动继承父类所有的便利构造函数。上面的例子都有体现。
可失败初始化器
定义类 结构体或者枚举初始化时可以失败在某些情况下可能会很有作用,这个失败可能会有以下几种方式触发。包括传入无效的形式参数值,或者缺少某种外部所需的资源。还有可能是其它阻止初始化的情况。
可失败的初始化器创建了一个初始化类型的可选值。你通过在可失败初始化器写 return nil 语句,来表明可失败初始化器在何种情况下会触发初始化失败。
struct Animal {
let species:String
init?(species:String) {
if species.isEmpty {
return nil
}
self.species = species
}
} if let animal = Animal(species:"") {
animal.species
}else {
print("传入的参数不正确 没被初始化成功")
}
枚举的可失败初始化器
你可以使用一个可失败的初始化器来带一个活着多个形式参数的枚举选择适合的情况。如果提供的形式参数没有匹配到合适的情况初始化器就可能失败。
代码示例
enum TemperatureUnit:Int {
case Kelvin = ,Celsius,Fahrenheit
init?(symbol:Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
} if let temp = TemperatureUnit(symbol:"C") {
temp.rawValue
}else {
print("没有初始化成功")
}
初始化失败的传递
类 结构体 枚举的可失败初始化器可以横向委托到同一个类 结构体或枚举里的另一个可失败初始化器。类似地,子类的可失败初始化器可以向上委托到父类的可失败初始化器。
无论哪种情况,如果你委托到另一个初始化器导致了初始化失败,那么整个初始化过程也会立即失败,并且之后任何初始化代码都不会执行。
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
} class CartItem: Product {
let quantity: Int
init?(name:String, quantity:Int ) {
if quantity < {
return nil
}
self.quantity = quantity
super.init(name: name)
}
} //如果你用不能为空 name 属性和数量为 1 或者更多来创建 CartItem 实例,则初始化成功:
if let twoSocks = CartItem(name: "sock", quantity: ) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
//如果你创建了一个 CartItem 实例, quantity 的值为 0 , CartItem 初始化器会导致初始化失败:
if let zeroShirts = CartItem(name: "shirt", quantity: ) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
} //类似地,如果你尝试创建一个 CartItem 实例,并且令 name 为空值,那么父类 Product 的初始化器就会导致初始化失败:
if let oneUnnamed = CartItem(name: "", quantity: ) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
重写可失败的初始化器
你可以在子类里重写父类的可失败初始化器。就好比其他的初始化器。或者,你可以用子类的非失败初始化器来重写父类的可失败初始化器,这样允许你定义一个初始化不会失败的子类,尽管父类的初始化允许失败。
但是需要注意的是,当你使用子类非失败的初始化器重写了一个可失败的初始化器,向上委托到父类初始化器的唯一方法是强制展开父类可失败初始化器的结果。
class Document {
var name: String? init() {} init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
} class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
} override init(name:String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
}else {
self.name = name
}
}
}
AutomaticallyNamedDocument 类用非可失败的 init(name:) 初始化器重写了父类的可失败 init?(name:) 初始化器。因为 AutomaticallyNamedDocument 类用不同的方式处理了空字符串的情况,它的初始化器不会失败,所以它提供了非可失败初始化器来代替。
必要的初始化器
在类的初始化器前添加required修饰符来表明所有的该类的子类都要实现该初始化器。
class SomeClass {
var name:String
required init(name:String) {
self.name = name
}
} class subClass:SomeClass {
required init(name: String) {
super.init(name: name)
}
}
当子类重写父类的必要初始化器时,必须在子类的初始化器前同样添加 required 修饰符以确保当其它类继承该子类时,该初始化器同为必要初始化器。在重写父类的必要初始化器时,不需要添加 override 修饰符。
通过闭包和函数来设置属性的默认值
class SomeClass {
//这个实际上是一个getter函数
var name:String {
//get {
// return "tianlanlan"
//}
//效果一样
return "tianlanlan" }
//通过闭包设置属性的默认值
var age:Int = {
return
}()
}
上面就是Swift中所有的初始化器的简单介绍。可以看到比OC中要丰富的多。另外Swift中也提供了一种反初始化机制。有点类似于OC中的dealloc方法。
反初始化
在类实例被释放的时候,反初始化器就会被立即调用。可以使用deinit关键字来写反初始化器。
反初始化的原理
当实例不在被需要的时候Swift会自动将其释放掉。以节省资源。代码示例
class Bank {
//静态变量 只有类本身才能调用
static var coinsInBank = 10_000
static func distribute(coins numberOfCoinsRequested: Int) -> Int {
let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receive(coins: Int) {
coinsInBank += coins
}
} class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.distribute(coins: coins)
}
func win(coins: Int) {
coinsInPurse += Bank.distribute(coins: coins)
}
deinit {
Bank.receive(coins: coinsInPurse)
}
} var playerOne: Player? = Player(coins: )
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// Prints "A new player has joined the game with 100 coins"
print("There are now \(Bank.coinsInBank) coins left in the bank")
//释放掉这个实例
playerOne = nil
Bank.coinsInBank //10000 说明调用了deinit的方法
Swift 学习笔记 (初始化)的更多相关文章
- 【swift学习笔记】二.页面转跳数据回传
上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...
- Swift学习笔记(一)搭配环境以及代码运行成功
原文:Swift学习笔记(一)搭配环境以及代码运行成功 1.Swift是啥? 百度去!度娘告诉你它是苹果最新推出的编程语言,比c,c++,objc要高效简单.能够开发ios,mac相关的app哦!是苹 ...
- swift学习笔记5——其它部分(自动引用计数、错误处理、泛型...)
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- swift学习笔记4——扩展、协议
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- swift学习笔记3——类、结构体、枚举
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- swift学习笔记1——基础部分
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- Swift学习笔记一
最近计划把Swift语言系统学习一下,然后将MagViewer用这种新语言重构一次,并且优化一下,这里记录一下Swift的学习笔记. Swift和Objective-C相比,在语法和书写形式上做了很多 ...
- 记录:swift学习笔记1-2
swift还在不断的更新做细微的调整,都说早起的鸟儿有虫吃,那么我们早点出发吧,趁着国内绝大多数的coder们还没有开始大范围普遍应用. 网上有些大神说:swift很简单!我不同意这个观点,假如你用h ...
- swift学习笔记2——函数、闭包
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
随机推荐
- ZOJ 1232 Adventure of Super Mario (Floyd + DP)
题意:有a个村庄,编号为1到a,有b个城堡,编号为a+1到a+b.现在超级玛丽在a+b处,他的家在1处.每条路是双向的,两端地点的编号以及路的长度都已给出.路的长度和通过所需时间相等.他有一双鞋子,可 ...
- cordova热更新插件调试
有更新www目录内容后,首先sencha app build,然后进入 cordova目录 运行 cordova-hcp build, 然后查看 chcp.json文件时间,然后压缩cordova目录 ...
- 修改linux iptable规则
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT(允许80端口通过防火墙)-A INPUT -m state --sta ...
- MFC中 报错:error : bitmap file Res\tankBattle.ico is not in 3.00 format
今天换了一个ico图标,本来源图像是bmp的,让我改了后缀名成ico. 然后编译就报错了:error : bitmap file Res\tankBattle.ico is not in 3.00 f ...
- 修改Linux基本配置
1.修改主机名 vi /etc/sysconfig/network NETWORKING=yes HOSTNAME=server1.cn 2.修改ip地址 vi /etc/sysconfig/netw ...
- 【共享单车】—— React后台管理系统开发手记:Router 4.0路由实战演练
前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. ...
- 2016.7.12 去除mybatis-generator生成的class里的注释
用mybatis-generator自动生成代码会出现很多没必要的注释. 在配置文件里加上: 是否去除所有自动生成的文件的时间戳: 是否去除所有自动生成文件的注释: <commentGe ...
- POJ-1190-生日蛋糕-DFS(深搜)-枚举-多重剪枝
题目链接: 这个题目非常好,有难度:能够好好的多做做: #include<iostream> #include<string> #include<cstdio> # ...
- Android世界第一个activity启动过程
Android世界第一个activity启动过程 第一次使用Markdown,感觉不错. Android系统从按下开机键一直到launcher的出现,是一个如何的过程,中间都做出了什么操作呢.带着这些 ...
- [Python] SQLBuilder 演示样例代码
用Python写一个SQLBuilder.Java版能够从 http://www.java2s.com/Code/Java/Database-SQL-JDBC/SQLBuilder.htm 看到. 附 ...