七、面向对象
swift可以定义
面向对象类型: 枚举 结构体 类 ,可以定义变量,枚举,结构体是值类型,类定义变量是引用类型
以上三种都支持定义存储属性,计算属性,方法,下标,构造器,嵌套类型。
JAVA支持的特性,swift都支持,不支持的,它也支持,语法简洁,但功能并不简单,反而很强大!
OOP :Object Oriented Programming 以对象为中心编程
对象总有状态和行为,对应的:
属性:描述对象的状态
方法:描述对象的行为
面向对象两大核心:类和对象
类:具有共同特征的对象的定义,如 人类, 对象是类的具体实例,你,我,乔布斯,雷蛋蛋
面向对象典型三大特征:
封装: 把对象的状态数据,实现细节隐藏起来,只露出合适的方法,允许外部程序调用,改变程序状态。 private ,internal public 限制封装
继承:复用已有的类,一般到特殊,苹果继承了水果,单继承,通过协议来弥补不足
多态:
swift 的 类 结构体 枚举 具有完全平等的地位
5种面向对象程序单元为:类,结构体,枚举,扩展,协议
注意:实例和对象不同
实例:结构体和枚举的实例
对象:类的实例
复杂的是:swift 中 类,结构体,枚举的内部分别可以定义属性(存储属性和计算属性),方法,下标,构造器,嵌套类型(嵌套类,嵌套结构体,嵌套枚举) 这5种类型成员!
真是让人 要疯了~
只有类支持继承
人之为学有难易乎?为之,则难者亦易矣,不为,则易者亦难矣!
枚举:管理一组有限的值的集合 enum 名字首字母大写,连续驼峰命名法
比如季节 性别
不同的是:成员不会默认分配一个整数值
enum Weekday
{
case Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
}
调用
var chooseDay = Weekday.Saturday
switch(chooseDay)
{
case .Monday:
println("星期一")
case .Tuesday:
。。。类似 省略
}
原始值:可以使 Int ,Double,Float,Character,String 类型的值
enum Weekday :Int
{
case Monday,Tuesday = 1,Wednesday=5,Thursday,Friday,Saturday,Sunday
}
enum Season :Character
{
case Spring = "S"
。。。
}
属性:rawValue 获取指定枚举值的原始值
var day = Weekday.Saturday
day.rawValue //8
关联值:associated value
类似属性,为每个枚举值定义更多的数据,从而更好地描述该枚举代表的实例的状态。
enum Planet
{
case Mercury(weight:Doubel,density:Double,name:String)
case Earth(Double,String)
}
调用
var p1 = Planet.Mercury(weight:0.05,density:5.43,name:"水星")
类 官方说,主要是使用类
private internal public final
类定义,最常用的 构造器,属性,方法
构造器:构造该类的实例
属性:定义该类或该类的实例所包含的状态数据
方法:定义该类或该类的实例的行为特征或者功能实现
类名后边()表示调用 构造器,从而返回该类的实例
计算属性:实例变量
存储属性:保存类型本身或实例的状态数据
构造器 init 不能声明返回值类型 无需写return
class Person
{
//定义存储属性
var name:String = ""
var age:Int = 0
//定义方法
func say(content:String)
{
println(content)
}
}
// 创建实例
var p:Person
// 调用构造器
p = Person()
//上边两句 简写 成
var p = Person()
// 实例的作用
1、访问实例属性
2、调用实例的方法
p.name = "renhairui"
p.say("swift是一门非常强大的语言")
大多数情况,定义一个类,就是为了重复创建类的实例,类是多个实例的共同特征
假如用上边的例子创建 结构体
struct Person
{
//定义存储属性
var name:String
var age:Int
//定义方法
func say(content:String)
{
println(content)
}
}
则
var my = Person(name:"renhairui",age:28)
println(my.name) // renhairui
假如调用无参数的构造器创建实例
var hello = Person()
则 hello.age 是0
引用类型比较 === !==
比较两个引用类型的变量是否指向同一个实例
只有引用类型的变量才能用 这个!
self关键字 指向该方法的调用者
实例方法中:self代表 调用该方法的实例
类型方法中:self代表代用该方法的类型(类或者结构体)
class Dog
{
func jump()
{
}
func run()
{
self.jump() //允许实例的成员调用另一个成员,可以省略self jump()正确!
}
}
在方法中强制引用存储属性 用self
class wolf
{
var name :String = ""
var age:Int = 2
init (name:String,age:Int)
{
self.name = name
self.age = age
}
func info ()
{
println("我的名字是(name),年龄是(age)岁")
}
}
调用
var wolf = Wolf(name:"任海瑞",age:28)
wolf.info()
self 作为方法调用者时,可以访问self,甚至当成返回值
class ReturnSelf
{
var age:Int = 0
func grow()->ReturnSelf
{
age ++
return self
}
}
调用
var rt = ReturnSelf()
rt.grow()
.grow()
println(rt.age)
结构体:封装少量相关的简单数据值,该实例在赋值和传递时会自动复制副本
存储属性: 1定义是指定初始值,2构造器中定义初始值
延迟存储 第一次被调用是才会被计算初始值的属性,lazy
lazy load 当某个实例持有另一个创建成本比较大的实例的引用时,降低内存开销,提升程序性能。
class Dept
{
var id:Int
var info:String
init (id:Int)
{
self.id = id
NSThread.sleepForTimeInterval(2)// 模拟耗时操作
self.info = "模拟读取数据库"
}
}
class User
{
var id:Int = 0
// 这个时候如果调用 Dept 则会创建较大成本实例 不能用 var dept = Dept(id :20)
lazy var dept = Dept(id:20)
var nicks = [String]()
}
//创建实例
var user =User()
user.nicks.append("孙悟空")
user.nicks.append("任海瑞")
println(user.nicks)
计算属性:本质是 getter setter方法的组合
class User
{
var first:String = ""
var last:String = ""
var fullName:String
{
// 定义计算属性的getter方法 返回值由first,last两个存储属性决定
return first +"-"+last
}
set(newValue){
// 改变first last两个存储属性
var names = newValue.componentsSeparatedBystring("-") //字符串切割成数组
self.first = names[0]
self.last = names[1]
}
}
init(first:String,last:String)
{
self.first =first
self.last = last
}
调用
let s = User(first:"海瑞",last:"任")
println(s.fullName) // 海瑞-任
s.fullName = "悟空-孙"
println(s.first) //悟空
println(s.last) //孙
setter方法提供隐式参数名 newValue 不用写 直接用
set {
var names = newValue.componentsSeparatedBystring("-") //字符串切割成数组
self.first = names[0]
self.last = names[1]
}
不写setter方法 就是只读的计算属性
属性观察者 willSet() didSet()
方法:行为特征的抽象
函数:放在枚举,结构体,类以外定义
方法:上边三种内定义,枚举,结构体中用static,类中用class,意思一样
类型.方法
实例.方法
class SomeClass
{
func test(){ //方法}
class func bar(#msg:String)
{
println("bar类型方法,传入参数:(msg)")
}
}
用
var sc = SomeClass()
//sc的test分离成函数 注意:千万不能加(),否则变成方法调用,不是将方法赋值给函数类型的变量
var f1:()->() = sc.test
var f2:(String)->Void = SomeClass.bar
// 下面两个一样
sc.test() f1()
SomeClass.bar(msg:"测试信息") f2("测试信息")
注意:结构体,枚举的实例方法不能分离,转换成函数,他们的类型方法才可以。
默认为方法除第一个参数外的其他形参都添加了外部形参名,不用写外部参数名或者#了,比较人性化。
enum,stuct,class中的func是实例方法
命名习惯:
方法名,以动词开头,动词+名词(描述第一个参数的含义)
有时候还会在动词和名词之间 加 with,for ,by介词,尽力把调用方法像一条简单的英文句子
func indexOfObject()
可变方法
结构体和枚举都是值类型,默认情况下,实例方法不能改变实例的存储属性。
假如需要修改,则mutating 声明为可变方法,可以在方法体内对隐式的self重新赋值
作用:改变枚举,结构体的内部数据。
struct Rect
{
var x:Int
var y:Int
var Int
var height:Int
mutating func moveByx(x:Int,y:Int)
{
self.x += x
self.y += y
}
}
// 创建实例
var rect = Rect(x:20,y:12,200,height:300)
// 调用可变方法
rect.moveByx(100,y:80)
println("(rect.x),(rect.y)")
下标:可以访问 对象,结婚,序列(枚举,结构体,类都支持)
P175