集合
存储方式离散,通过hash方式存储。集合中没有重复元素
var b: Set<Int> = [1, 2, 3, 5, 2, 1] //创建一个集合
print(b) //输出结果为[3, 5, 2, 1]
b.insert(5) //添加元素
b.remove(2) //删除元素
for x in b //对集合b遍历
{
print(x)
}
var c: Set<Int> = [1, 4, 5, 9, 1]
print(b.intersect(c)) //交集(b,c都有的元素)
print(b.union(c)) //并集(b,c所有的元素)
print(b.subtract(c)) //差集(b有,c没有的元素)
print(c.isSubsetOf(b)) // 判断c是不是b的子集
print(b.isSupersetOf(c)) // 判断b是不是c的超集
字典(存放键值对组合的容器)
字典中的每个元素都是由两部分构成,冒号前面是键,后面是值
var dict = ["abacus": "算盘", "abnormal": "异常的", "hello": "你好", "OK": "好的"] //创建一个字典
print(dict["OK"]) //通过键获取对应的值(可空类型,因为给的键可能没有与之对应的值)
print(dict["OK"]!) //key --> value
print(dict["asd"]) //为空时 nil
dict["fruit"] = "banana" //添加元素,需要键和值
dict.removeValueForKey("hello") //删除元素,值删除键,值会被自动删除
dict["OK"] = nil //也可以把键对应的值赋值为nil,也能删除
for x in dict.values //字典的遍历
{
print(x)
}
for x in dict.keys
{
print("\(x) ---> \(dict[x])")
}
//最常用,通过元组获得字典中的键和值
for (key, value) in dict
{
print("\(key) -> \(value)")
}
重点
函数
//定义函数 func 函数名(参数列表)-> 返回类型 { 函数的执行体 }
func sayHello(name: String,/* 默认类型let */ alreadyGreeted: Bool = false/*给参数添加默认值,如果调用函数时,没有给该参数赋值,就是用默认值*/) -> String
{
//如果函数的返回类型不是Void,那么函数中一定有return语句
//return greeting
if alreadyGreeted
{
return "怎么又是你, " + name + "!"
}
else
{
return "你好, " + name + "!"
}
}
print(sayHello("王大锤"))
//调用函数时,从第二个参数开始要写外部参数名
//函数名(外部参数名 内部参数名:类型, 外部参数名 内部参数名: 类型)
//如果不想写函数名,定义函数时,在参数名前面加"_"(name: String,_ alreadyGreeted: Bool)
//如果想写第一个函数名,在第一个函数名前面再加一个外部参数名
let str = sayHello("王大锤",alreadyGreeted: false)
print(str)
函数名的其他用法
func sum(a a: Int = 0, b: Int = 0, c: Int = 0) ->Int
{
return a + b + c
}
print(sum(a: 1,b: 2,c: 3))
print(sum())
print(sum(a: 100)) //b = 0, c =0
print(sum(a: 100,b: 200)) //c = 0
print(sum(c: 100, b: 50)) //a = 0
//可变参数列表 "Int..."(参数的个数是任意多个)
func a(nums: Int...) -> Int
{
var total = 0
for num in nums
{
total += num
}
return total
}
print(a())
print(a(999))
print(a(1, 2, 3))
//使用元组让函数一次返回多条数据
func minMax(array: [Int]) -> (min: Int, max: Int)?
{
if array.count == 0 //判断所传数组是否为空
{
return nil
}
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count]
{
if value < currentMin
{
currentMin = value
}
else if value > currentMax
{
currentMax = value
}
}
return (currentMin, currentMax)
}
if var b = minMax([1, 2, 5, 745, 75, 34])
{
print(b.max)
print(b.min)
}
使用inout输入输出参数(不仅将数据传入函数,还要从函数中取出数据)
func swap(inout x: Int,inout y: Int) -> Void
{
(x, y) = (y, x)
}
var x = 5, y = 10
//函数调用传参都是传值
swap(&x, y: &y)
print("x = \(x)")
print("y = \(y)")
func create(inout x: Int)
{
x = 1000
}
var x = 1
create(&x)
print(x)
应用
用一个函数根据系统时间返回不同的问候语
func sayHello(name: String) -> String {
//获取系统当前时间
let date = NSDate()
let cal = NSCalendar.currentCalendar()
let hour = cal.component(.Hour, fromDate: date)
var greeting: String
switch hour {
case 0...6: // 不同的分支可以有重叠的部分
greeting = "还没睡"
// fallthrough // 继续执行下一个case
case 7...10: // 匹配了一个分支之后不再匹配其他的分支
greeting = "早上好"
case 11...13:
greeting = "中午好"
case 14...18:
greeting = "下午好"
default:
greeting = "晚上好"
}
return name + ", " + greeting + "!"
}
在一个函数中调用另一个函数
func sum(a: Int, b: Int) -> Int
{
return a + b
}
func mul(a: Int, b: Int)-> Int
{
return a * b
}
var fn1 = sum
fn1 = mul
//赋给fn的函数必须和之前的类型一样,这里类型为(Int, Int) -> Int)
//在swift中函数是一种类型
//这也就意味着函数可以作为变量或常量的类型
//同理函数也可以作为另一个函数的参数或返回值
//在创建函数时,将参数写成函数的形式fn: (Int, Int) -> Int
func foo(x: [Int], fn: (Int, Int) -> Int) -> Int
{
var sum = x[0]
for a in x[1..<x.count]
{
sum = fn(sum, a) // 对数组进行累加运算
}
return sum
}
let a = [1, 2, 3, 4, 5]
print(foo(a, fn: sum)) //调用sum函数作为其中一个参数
//调用foo函数时第二个参数可以传什么
//1.所有自定义的(Int, Int) -> Int类型的函数
//2.传入二元运算符:+-*/%(因为运算符也是函数)
//也就是可以写成 print(foo(a, fn: +))
//3.传入匿名函数(闭包)
递归函数(一个函数直接或间接的调用自身)
2个关键条件
1.递归公式
2.收敛条件
//求阶乘
if n == 0 || n == 1 //收敛条件
{
return 1
}
return Double(n) * sum(n - 1)//递归算法(算法)
}
print(sum(4))
闭包
闭包 - 就是Java和C#中的Lambda函数,JS中的匿名函数,c/c++/OC中的block起相同作用的东西,它存在的意义是实现代码的解耦合
var array = ["game", "ok", "hello", "cat", "banana", "good"]
//按字母都少进行排序
array.sortInPlace{ (one, two) -> Bool in
return one.characters.count < two.characters.count
}
//如果函数的最后一个参数是闭包可以写成尾随闭包的形式
//也就是将闭包放在函数参数的圆括号外面写在一堆花括号里面
//如果函数后面有尾随闭包且函数的圆括号中没有参数
//那么函数的圆括号也可以省略(仅限于有尾随闭包的场景)
array.sortInPlace{
$0.characters.count < $1.characters.count
}
array.sortInPlace() { $0 > $1 }(尾随闭包)
print(array)
闭包在数组中的三种重要应用
let array = [32, 45, 94 ,64, 65, 47, 12, 18]
print(array)
//1.过滤
//保留大于50的数
let newArray1 = array.filter { $0 > 50 }
print(newArray1)
//保留偶数
let newArray2 = array.filter { $0 % 2 == 0 }
let newArray2 = array.filter { (x: Int) -> Bool in
return x % 2 == 0
}
print(newArray2)
//2.映射
//将数组中的元素平方
let newArray3 = array.map { (x: Int) -> Int in
return x * x
}
let newArray3 = array.map { $0 * $0 }
print(newArray3)
//3.缩减
//初始值为0 并把数组的所有元素相加
let newArray4 = array.reduce(0, combine: +)
print(newArray4) //结果为一个数
//找出数组中的最大值
let newArray5 = array[1..<array.count].reduce(array[0]) { $1 > $0 ? $1 : $0 }
print(newArray5)
类
对于Swift中没有的类,需要自己定义
//步骤1 定义类
//定义类就可以创建出新的类型
class Student //创建一个学生类
{
//变量定义到类的外面就叫变量 - variable
//变量定义到类的里面就叫属性 - property
//数据抽象 - 找到和学生相关的属性(找名词)
var name: String //类的属性
var age: Int
//函数写到类的外面叫函数 - function
//函数写到类的里面叫方法 - method
//行为抽象 - 找到和学生相关的方法(找动词)
//初始化方法(构造方法/构造器) - constructor
init(name: String, age: Int)
{
self.name = name
self.age = age
}
//事件
func eat(foodName: String)
{
print("\(name)正在吃\(foodName)")
}
func study(courseName: String)
{
print("\(name)正在学习\(courseName)")
}
func watchJapaneseAV()
{
if age >= 18
{
print("\(name)正在观看动作片")
}
else
{
print("亲爱的\(name),推荐你看《熊出没》")
}
}
}
//步骤2 创建对象(调用初始化方法)
let stu1 = Student(name: "王大锤", age: 18)
//步骤3 给对象发消息(通过给对象发消息来解决问题)
stu1.eat("饭")
stu1.study("swift")
stu1.watchJapaneseAV()
类的定义(补充)
初始化的多种便利用法
属性访问控制的用法
class Piont {
//public var x: Double 添加public,则该属性可以被可以被任何人使用,修改
//internal(默认访问级别,internal修饰符可写可不写)internal访问级别所修饰的属性或方法在源代码所在的整个模块都可以访问。如果是框架或者库代码,则在整个框架内部都可以访问,框架由外部代码所引用时,则不可以访问。
//private访问级别所修饰的属性或者方法只能在当前的Swift源文件里可以访问。不允许其属性。存储属性通常是private的,因为数据要保护起来
var x: Double
var y: Double
// 指派初始化方法 / 指派构造器
// 被其他初始化方法调用的初始化方法
init(x: Double, y: Double)
{
self.x = x
self.y = y
}
//可以在一个类中定义多种初始化方法
//便利初始化方法(调用其他初始化方法的初始化方法)
convenience init()
{
self.init(x: 0, y: 0)
}
convenience init(Piont: (Double, Double))
{
self.init(x: Piont.0, y: Piont.1)
}
func distanceTo(other: Piont) -> Double
{
let dx = x - other.x
let dy = y - other.y
return sqrt(dx * dx + dy * dy)
}
func moveTo(x: Double, y: Double)
{
self.x = x
self.y = y
}
}
类的扩展
如果在某个特定的应用场景中你发现现有的类缺少了某项功能
那么可以通过类扩展(extension)的方式现场添加这项功能
extension Piont//这里因为要在画板中添加三角形的坐标,但是定义的类的数据类型与CGPoint类型不同,如果在类里面的方法中转换类型,会很麻烦,所以在这里通过类扩展来直接添加转换功能
{
var cgPoint: CGPoint
{
get { return CGPointMake(CGFloat(x), CGFloat(y)) }
}
}
class Triangle {
var va: Piont
var vb: Piont
var vc: Piont
var color: UIColor?
init(va: Piont, vb: Piont, vc: Piont)
{
self.va = va
self.vb = vb
self.vc = vc
}
var perimeter: Double
{
get
{
let ab = va.distanceTo(vb)
let bc = vb.distanceTo(vc)
let ca = vc.distanceTo(va)
return ab + bc + ca
}
}
var area: Double
{
get
{
let ab = va.distanceTo(vb)
let bc = vb.distanceTo(vc)
let ca = vc.distanceTo(va)
let halfP = perimeter / 2
return sqrt(halfP * (halfP - ab) * (halfP - bc) * (halfP - ca))
}
}
func draw()
{
let bezierPath = UIBezierPath()
bezierPath.moveToPoint(va.cgPoint)
bezierPath.addLineToPoint(vb.cgPoint)
bezierPath.addLineToPoint(vc.cgPoint)
bezierPath.addLineToPoint(va.cgPoint)
(color ?? UIColor.blueColor()).set() //如果不为空,用问号前面,为空用后面
bezierPath.fill()
}
}
函数的注释信息格式
作用:在调用给函数时,会根据注释信息给出函数的参数意义,返回值的意义等说明
/**
学习
- parameter courseName: 课程的名称
- parameter hour: 学习时间
- returns: 学会了返回true否则返回false
*/
public func study(courseName: String, hour: Int) -> Bool {
print("\(_name)正在学习\(courseName).")
return hour > 180 ? true : false
}
运算符重载
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
func gcd(x: Int, _ y: Int) -> Int {
if x > y {
return gcd(y, x)
}
else if y % x != 0 {
return gcd(y % x, x)
}
else {
return x
}
}
class Fraction {
private var _num: Int
private var _den: Int
var info: String {
get {
return _num == 0 || _den == 1 ? "\(_num)" : "\(_num)/\(_den)"
}
}
init(num: Int, den: Int) {
_num = num
_den = den
simplify()
normalize()
}
func add(other: Fraction) -> Fraction {
return Fraction(num: _num * other._den + other._num * _den, den: _den * other._den)
}
func sub(other: Fraction) -> Fraction {
return Fraction(num: _num * other._den - other._num * _den, den: _den * other._den)
}
func mul(other: Fraction) -> Fraction {
return Fraction(num: _num * other._num, den: _den * other._den)
}
func div(other: Fraction) -> Fraction {
return Fraction(num: _num * other._den, den: _den * other._num)
}
func normalize() -> Fraction {
if _den < 0 {
_num = -_num
_den = -_den
}
return self
}
func simplify() -> Fraction {
if _num == 0 {
_den = 1
}
else {
let x = abs(_num)
let y = abs(_den)
let g = gcd(x, y)
_num /= g
_den /= g
}
return self
}
}
//在定义一个类后,类中的方法涉及到+ - * %等运算时
例如加法:
let a = Fraction(num: 3, den: -2)
let b = Fraction(num: 3, den: 2)
print(a.add(b))
//写法不够直观,简洁
//此时,可以使用运算符重载,对已有的运算符重新进行定义,赋予其另一种功能
func -(one: Fraction, two: Fraction) -> Fraction
{
return one.sub(two)
}
func *(one: Fraction, two: Fraction) -> Fraction
{
return one.mul(two)
}
func /(one: Fraction, two: Fraction) -> Fraction
{
return one.div(two)
}
//定以后,就可以直接使用 "+"对 Fraction这个类的进行运算
print(a + b)
类的继承
继承: 从已有的类创建新类的过程
提供继承信息的称为父类(超类/基类)
得到继承信息的称为子类(派生类/衍生类)
通常子类除了得到父类的继承信息还会增加一些自己特有的东西
所以子类的能力一定比父类更强大
继承的意义在于子类可以复用父类的代码并且增强系统现有的功能
//先创建一个Person
enum Gender {
case Male
case Female
}
class Person {
var name: String
var age: Int
var gender: Gender
init(name: String, age: Int, gender: Gender) {
self.name = name
self.age = age
self.gender = gender
}
func eat() {
print("\(name)正在吃饭.")
}
}
//然后在它的基础上再创建一个Teacher
//Teacher这个类的父类是Person,它得到父类的继承信息,并且在Person已有的基础上添加新的元素
class Teacher: Person {
var title: String
init(name: String, age: Int, gender: Gender, title: String) {
self.title = title
super.init(name: name, age: age, gender: gender)
}
func teach(courseName: String) {
print("\(name)\(title)正在教\(courseName).")
}
}
//在Person的基础上再创建一个Student
class Student: Person {
var major: String
init(name: String, age: Int, gender: Gender, major: String) {
self.major = major
super.init(name: name, age: age, gender: gender)
}
func study(courseName: String) {
print("\(name)是\(major)专业的学生.")
print("\(gender == .Male ? "他" : "她")正在学习\(courseName).")
}
}
//创建2个对象
let p1 = Person(name: "王大锤", age: 18, gender: .Male)
// 可以将子类型的对象赋值给父类型的变量(因为子类跟父类之间是IS-A关系)
// 学生是人, 老师是人, 所以学生和老师的对象可以赋值给人类型的变量)
let p2: Person = Student(name: "张全蛋", age: 19, major: "啪啪啪", gender: .Female)
//如果要将父类型的变量转换成子类型,需要用as运算符进行类型转换
//如果能够确定父类型的变量中就是某种子类型的对象可以用as!转换
//如果不确定父类型的变量中是哪种子类型,可以用as?尝试转换
(p2 as! Student).study("嘿嘿嘿")
if let temp = p2 as? Teacher
{
temp.teach("H5")
}
else
{
print("\(p2.name)不是老师")
}
let p3 = Teacher(name: "豆豆", age: 28, title: "砖家", gender: .Male)
p3.teach("swift")
类的继承 多态
//先自己定义一个父类,几个子类
//这里的父类为 Pet 子类有Dog,Cat,Mistress
let D = Dog(nikeName: "旺财", gender: .Male, age: 5, variety: "未知", hairColor: "red")
let C = Cat(nikeName: "狗蛋", gender: .Male, age: 6, hairColor: "blank")
let meinv = Mistress(nikeName: "爱叫", gender: .Female, age: 22, height: 165, cup: "E")
//创建一个数组,数组中用多个不同类型,但都属于一个父类
//此时,Swift的类型推断会将这个数组的类型推断为Pet(父类)
let petArray = [D, C, meinv]
//同样的对象类型(pet类型)接收相同的消息(调用相同的方法)
//但是做了不同的事情
//这就是多态(polymorphism)
//实现多态的关键步骤
//1.方法重写(子类在继承父类的过程中对父类已有的方法进行重写。而且不同的子类给出各自不同的实现版本)
//2.对象造型(将子类对象当成父类型来使用)
for pet in petArray
{
pet.shout()
pet.eat()
pet.play()
//可以通过if+as?将父类型安全的转换成子类型然后在调用子类型特有的方法
if let cat = pet as? Cat
{
cat.catchTheMouse()
}
if let mm = pet as? Mistress
{
mm.papapa()
}
}
面向对象七原则:
1. 单一职责原则(SRP)
它规定一个类应该只有一个发生变化的原因。所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。
2. 开闭原则(OCP)
开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即修改原有的代码对外部的使用是透明的。
3. 依赖倒转原则(面向抽象编程, DIP)
一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化。简单的说,子类型能够替换掉它们的父类型。
4. 里氏替换原则(LSP) - 能用父类型的地方就一定可以使用子类型
5. 接口隔离原则(ISP)
一个类对另外一个类的依赖性应当是建立在最小的接口上的。
一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染
6. 合成聚合复用原则(CARP)
该原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分:新的对象通过向这些对象的委派达到复用已有功能的目的。
7. 迪米特法则(LoD)
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。