Swift的第二周
Day08.08
1.Set(集合)
定义集合:
var a : Set<Int> = [1,2,9,5,7,3,2]
var b : Set<Int> = [1,4,2,54,6,7]
删除集合中的元素:
a.remove(2)
添加一个元素:
a.insert(111)
求两个集合的交集:
print(a.intersect(b))
求两个集合的并集:
print(a.union(b))
求两个集合的差集(a有b没有的元素):
print(a.subtract(b))
判断两个集合是否相交:
print(a.isDisjointWith(b))
2.Dictionary(字典)
定义字典:
var dict:[String:String] = [“abnormal":"异常的","good":"好哒","hello":"你好","abacus":"算盘”]
从字典中取值:
print(dict[“hello”]!) 没有!取得的值为optional
添加元素:
dict[“normal”] = “正常的”
删除元素:
dict.removeValueForKey(“hello”)等同于dict[“hello”] = nil
相当于在hello的位置赋个空值(nil)
遍历字典中所有的键
for key in dict.keys{
print("\(key)--->\(dict[key])")
}
直接通过一个元组获得字典中的键和值(原始类型)
for (key,value) in dict{
print("\(key) --->\(value)")
}
var a : Set<Int> = [1,2,9,5,7,3]
print(a)
3.Function(函数)
(1)函数的参数名
内部参数名:函数内部使用;外部参数名:调用函数时使用。
可以使用_来作为外部参数名表示省略外部参数名
函数名(外部参数名 内部参数名:类型,外部参数名 内部参数名:类型)
func myMin(a x:Int,b y:Int) ->Int{
return x<y ? x : y
}
print(myMin(a:38, b: 5))
print(myMin(a:399, b: 250))
(2)定义函数
func 函数名(参数列表)-> 返回类型{函数的执行体}
可以给参数执行默认值
如果调用函数的时候没有给参数赋值就直接使用默认值
func sayHello(personName:String,_ already:Bool = false) ->String{
//如果函数的返回类型不是void 那么函数中一定有return语句
if already {
return "咋个又是你哦," + personName + "!"
}else{
return "你好," + personName + "!" + "欢迎您!"
}
//return "Hello," + personName + "!"
}
//调用函数
//函数名(参数值)
//调用Swift函数时,在默认的情况下从第二个函数开始需要写参数名
print(sayHello("王钢炮 ", true))
//如果没有给第二个参数赋值那么就直接使用默认值false
print(sayHello("赵云 "))
func sum(a: Int = 0,_ b: Int = 0,_ c: Int = 0) ->Int{
return a + b + c
}
print(sum(1,2,3))
print(sum(100, 200, 299))
Swift中函数的参数列表可以是可变参数列表(参数的个数可以是任意多个)
func sum(nums: Int...) ->Int{
var total = 0
for num in nums{
total += num
}
return total
}
print(sum(1,2,3,4,54,5,6,7,1,3))
print(sum(998))
inout函数
inout - 输入输出参数(不仅将数据传入函数还要从函数中取出数据)
func creatX(inout x: Int){
x = 1000
}
var x = 1
creatX(&x)
print(x)
inout函数传参前要加上&符号
取系统时间
let date = NSDate()
let cal = NSCalendar.currentCalendar()
let hour = cal.component(.Hour, fromDate: date)
fallthrough
switch hour{
case 0...6:
// 不同的分支可以有重叠的部分
greeting = "怎么还没睡呀"
//fallthrough//继续执行下一个case
//匹配一个分支后不再匹配其他的分支
case 5...10:
greeting = "早起的鸟儿有虫吃"
Day08.09
1.函数作为类型
在swift中函数是一种类型
这也就意味着函数可以作为变量或常量的类型
同理函数也可以作为另一个函数的参数或返回值
func sum(a:Int,_ b:Int) ->Int{
return a + b
}
func mul(a:Int,_ b:Int)->Int{
return a * b
}
func arraySum(array:[Int])->Int{
var sum = 0
for x in array { sum += x
}
return sum
}
//var fun = sum
//
//fun = mul
//print(fun(3,5))
//print(sum(3, 5))
var fun = arraySum
print(fun([2,5,2,6,3]))
2.filter(过滤)
过滤,使用它来选择数组中满足条件的元素filter(includeElement:(T)->Bool)->T[]接受一个数组元素,返回一个Bool类型
let array = [23,37,96,55,40,92,68,88]
let newArray = array.filter { $0 > 50 }
print(newArray)
let neww = array.filter { $0 % 2 == 0}
print(neww)
3.Map(映射)
映射,获取一个闭包表达式作为其唯一参数。数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。具体的映射方式和返回值类型由闭包来指定。
当提供给数组闭包函数后,Map方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。
let array = [25,33,72,43,28,99]
let newArray1 = array.map { (x:Int) -> Int in
return x / 2
}
print(newArray1)
4.reduce(缩减)
缩减方法吧数组元素组合计算为一个值,并且会接受一个初始值,这个初始值的类型可能和数组元素类型不同。
如过初始值是一个字符串使用加法,那么结果是将字符串拼接起来。
let newArray2 = array.reduce(0, combine: -)
print(newArray2)
let newArray3 = array.reduce(1, combine: *)
print(newArray3)
let max = array[0]
let newArray4 = array.reduce(max) {
$1 > $0 ? $1 : $0
}
print(newArray4)
let strArray = ["I","love","you"]
let array5 = strArray.reduce("") {$0 + " " + $1}
print(array5)
当数据比较大的时候,高阶函数会比传统实现更快,因为它可以并行执行(如运行在多核上),除非真的需要更高定制版本的map,reduce,filter,否则可以抑制使用它们已获得更快的执行速度。
5.类(class)
(1).发现类
- 在对问题的描述中找名次和动词
- 名词会成为类或者类中的属性 动词会成为类中的方法
(2).定义类
- 数据抽象(属性)
- 行为抽象(方法)
- 初始化方法
(3): 创建对象(调用初始化方法)
let stu = Student(name: "骆昊", age: 35)
(4): 给对象发消息(通过给对象发消息来解决问题)
步骤1:定义类(如果你要用的类苹果已经提供了就跳到第二步)
定义类就可以创建出新的类型
class Student{
//数据抽象- 找到和学生相关的属性(找名词)
var name:String
var age: Int
//初始化方法-constructor
init(name:String,age:Int){
self.name = name
self.age = age
}
//行为抽象- 找到和学生相关的方法(找动词)
func eat(foodname: String) -> Void{
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: 35)
步骤3: 给对象发消息(通过给对象发消息来解决问题)
stu1.eat("啃的鸡")
stu1.study("Swift")
stu1.watchJapaneseAV()
let stu2 = Student(name: "王钢炮", age: 13)
stu2.eat("shit")
stu2.study("世界历史")
stu2.watchJapaneseAV()
6.访问修饰符
- public (公开)————方法一般是public的,因为方法是对象接受的消息
- internal (内部的) - 默认,直接使用默认的internal修饰符表示在本项目中公开对其他项目私有
- private (私有)————数据都是私有的,存储属性通常是private的,因为数据要保护起来
如果自定义的类没有打算在其他项目中使用 可以不写访问修饰符
Day08.10
1.extension(类扩展)
如果在某个特定的应用场景中发现现有的类缺少了某项功能
那么可以通过类扩展(extension)的方式现场添加这项功能
extension Point{
var cgPoint: CGPoint{
get{return CGPointMake(CGFloat(x), CGFloat(y))}
}
}
swift中的终极方案(to 空类型?)
如果color为nil就取??后面的值。如果color不为nil就直接使用color的值
if let color = color{
color.set()
}else{
UIColor.blueColor().set()
}
//上下代码实现功能相同
(color ?? UIColor.blueColor()).set()
2.stored property(存储属性)
保存和类相关的数据的属性
3.computational property(计算属性)
在某些类中,一些属性不能直接得到,但是可以通过对存储属性进行计算获取,这种就叫做计算属性
class Circle {
//存储属性(保存和圆相关的数据的属性)
var radius: Double
var center: Point
init(radius:Double,center:Point){
self.radius = radius
self.center = center
}
// 计算属性(通过对存储属性做运算得到的属性)
var perimeter:Double{
get{return 2 * M_PI * radius}
}
var area:Double{
get {return M_PI * radius * radius}
}
}
4.convenience(便利化方法)
class Point {
var x :Double
var y :Double
//便利初始化方法
//调用其他初始化方法的初始化方法
convenience init(){
self.init(x: 0,y: 0)
}
convenience init(point:(Double,Double)){
self.init(x: point.0,y: point.1)
}
//指派初始化方法
//被其他初始化方法调用的初始化方法
init(x:Double,y:Double){
self.x = x
self.y = y
}
Day08.11
创建一个分数类,写出计算方法
//Greatest common Divisor
//找最大公约数的函数
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
normalize()
simplify()
}
func add(other:Fraction)->Fraction{
return Fraction(num: _num*other._den + other._num*_den, den: _den*other._den).simplify().normalize()
}
func sub(other:Fraction)->Fraction{
return Fraction(num: _num*other._den - other._num*_den, den: _den*other._den).simplify().normalize()
}
//级联编程 将一长段代码用一句话实现,实现代码的简洁
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
}
}
//运算符重载(为自定义的类型定义运算符)
func +(one:Fraction,two:Fraction )->Fraction{
return one.add(two)
}
func -(one:Fraction,two:Fraction )->Fraction{
return one.sub(two)
}
使用运算符重载前
let a = Fraction(num: 4, den: 7)
let b = Fraction(num: 4, den: 9)
print((a.add(b)).info)
print((a.sub(b)).info)
使用运算符重载后
let a = Fraction(num: 4, den: 7)
let b = Fraction(num: 4, den: 9)
print((a+b).info)
print((a-b).info)
文档注释
基本格式
/**
declaration
paratemer
return
*/
枚举
GET: 枚举是定义符号常量的最佳方式
GET: 符号常量总是优于字面常量
花色的枚举
- parameter Spade:黑桃
- parameter Heart:红心
- parameter Club:草花
- parameter Diamond:方块
enum Suite:String{
case Spade = "♠️",Heart = "♥️",Club = "♣️",Diamond = "♦️"
}
Day08.12
继承
继承:从已有的类创建新类的过程。
——提供继承信息的称为父类(超类/基类)
——得到继承信息的称为子类(派生类/衍生类)
通常子类除了得到父类的继承信息还会增加一些自己特有的东西
所有子类的能力一定比父类更强大
继承的意义在于子类可以复用父类的代码并且增强系统现有的功能
class Teacher:Human {//定义一个子类teacher,父类是Human
var title:String
init (name:String,age:Int,title:String, gender:Gender){
self.title = title
super.init(name: name, age: age, gender: gender)
//初始化方法,调用他爸爸(父类)的初始化方法,title为teacher特有的属性,单独初始化,其他的属性直接调用父类
}
我们可以将子类型的对象赋值给父类型的变量(因为子类跟父类之间是IS- A关系)
学生是人,老师是人,所以学生和老师的对象可以赋值给人类型的变量
let h1 = Human(name: "孙悟空", age: 999, gender: .Male)
h1.eat()
如果要将父类型的变量转换成子类型的时候需要用as运算符进行类型转换
如果能够确认父类型的变量中就是某种子类型的对象可以用as!进行转换
如果不确定父类型的变量中是哪种子类型可以用as?尝试转换
let h2:Human = Student(name: "马化腾", age: 43, major: "互联网", gender: .Male)
h2.eat()
(h2 as! Student).study("制作一个企鹅")
let h3:Human = Teacher(name:"范跑跑",age:38,title:"人名教师",gender:.Male)
(h3 as? Student).study("田径运动")
()
重写
父类有的方法子类可以重现实现 这个过程叫方法重写
需要在方法前添加override关键字
重写有时候也被称为置换/覆盖/覆写
func shout(){
print("\(nickname)发出了叫声。")
}
//父类中的shout
override func shout() {
print("\(nickname): 喵喵喵...")
}
//子类中的shout重写
多态(polymorphism)
同样的对象类型(Pet类型)接收相同的消息(调用相同的方法)
但是做了不同的事情 这就是多态
if let dog = pet as? Dog{
dog.watchDoor()
}else if let cat = pet as? Cat{
cat.catchTheMouse()
}
实现多态的关键步骤:
1.方法重写(子类在继承父类的过程中对父类已有的方法进行重写,而且不同子类给出各自不同的实现版本)
2.对象造型(将子类对象当成父类型来使用)
可以通过if as?将父类安全的转换成子类型然后再调用子类特有方法
面向对象编程的原则
终极原则:高内聚,低耦合
面向对象七原则:
1.单一职责原则(SRP)-每一个类应该专注于做一件事。
2.开闭原则(OCP) - 面向扩展开放,面向修改关闭。
3.依赖倒转原则(面向抽象编程,DIP),定义方法参数类型的时候尽可能使用父类型(抽象类型),因为如果用父类型的参数调用方法时可以传入任意子类型对象
4.里氏替换原则(LSP) - 能用父类型的地方就一定可以使用子类型
5.接口隔离原则(ISP) - 应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。
6.合成聚合复用原则(CARP) - 尽量使用合成/聚合达到复用,尽量少用继承。原则:一个类中有另一个类的对象。
7.迪米特法则(LoD) - 又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。
面向对象编程设计模式
四人帮设计模式(GoF)-23种设计模式
工厂模式(Factory), 工厂方法模式(Factory Method),单例模式(Singleton), 外观(Facade)模式, 观察者(Observer)模式,桥接(Bridge)模式都是比较常用的,不同的项目有不同的设计方向,可以参考的设计模式也不尽相同,没有定数,只是上面这几个模式用的比较多一些。
全部的23种设计模式:
1.工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。
2.建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。
3.工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
4.原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。
5.单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。
结构型模式
6.适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。
7.桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。
8.合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。
9.装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。
10.门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。
11.享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。
12.代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
13.责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
14.命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。
15.解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。
16.迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。
17.调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。
18.备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
19.观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
20.状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。
21.策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
22.模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。
23.访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。
学习总结:
学习Swift的第二周,函数,类,继承多态什么的一大串确实让人有点恼火,很多东西好像能理解的样子,但实际应用起来又发现都不会用,知识量这么大,感觉确实应该更加努力, 不会用的知识,敲得多了,自然也就会了💪。