#Swift函数和类

函数

函数是一种独立的可以重复使用的功能模块。是解决重复代码的一种很有效的方法,下面直接来介绍swift的函数:
1.常量参数

// func 关键字 sayHello 函数名 () 参数 {}函数体,执行操作的代码
func sayHello(name:String) -> String{ //-> String为返回类型
    return "hello,\(name)"  //如果返回类型不为Void,必须返回与类型匹配的值
}
//参数默认是常量,不能在函数体内修改 name:String 相当于let name:String
func sayHello(var name:String) -> String{
//参数也可以设置为变量,但这种方式是不好的,在swift3.0中要去除这种语法
   name = "你" 
}

2.参数的默认值

//在定义函数的时候,可以给参数设置一个默认值
func sum(a:Int=0,b:Int=0,c:Int=0) -> Int{
    let total = a + b + c
    return total
}
//有默认值的参数可以不传值
print(sum(1)) //结果就是1,传入1给参数a赋值。相当于1+0+0

3.参数列表(传入可变参数个数)

//参数为一个列表,参数的个数可以变化
func sum (nums:Int...) -> Int{ 
    var total = 0
    for num in nums{
        total += num
    }
    return total
}
//调用的时候可以传入任意个参数
print(sum(1,2,3,5,6,7))

4.外部参数名和内部参数名

//y:Int 其实是省略了外部参数名,所以内部参数名和外部参数名默认一样
//相当于(x:Int,y y:Int的写法)
func sum (x:Int,y:Int) -> Int{
    return x+y
}
//调用的时候第一个参数默认不需要写参数名,第二个以后每个都需要写参数名
print(sum(5,y:3)) 

//如果需要让调用的时候不用写第二个参数名也可以写(x:Int,_ y:Int)
//_ 下划线表示省略外部参数名
func sum1 (x:Int,_ y:Int) -> Int{
    return x+y
}
print(sum(5,3))

5.函数返回多个值

let array = [1,5,7,9,6]
//如果需要函数返回多个值,可以用元组来进行返回
func minMax(array:[Int]) -> (Int,Int){
    var reallymin = array[0]
    var reallymax = array[0]
    for i in array{
        if i < reallymin{
            reallymin = i
        }
        else if i > reallymax{
            reallymax = i
        }
    }
    return (reallymin,reallymax)
}
let tuple = minMax(array)
print(tuple.0) //结果为1

6.函数输入输出参数

//通过inout才能改变函数外部的值
//inout - 输入输出参数(不仅将数据传入函数还要从函数中取出数据)
func swap(inout a:Int,inout  b:Int) -> Void{   
   (a,b)=(b,a)
}
var x = 5
var y = 10
swap(&x, b:&y) //调用的时候参数的值一定要加&
print(x,y)

7.函数类型的参数

//定义了一个两个数相加的函数和一个相乘的函数
func add (a:Int,_ b:Int) -> Int{
    return a+b
}
func multiply(a:Int,b:Int){
    return a*b
}
var array = [12,13,45,67]
//该函数的第二个参数就是函数类型
func arrayAdd (array:[Int],_ fn:(Int,Int) -> Int){
    var sum = array[0]
    for i in array[1..<array.count]{
        sum = fn(sum,i) //通过传入的函数可以做任意的二元运算
    }
    return sum
}
print(arrayAdd(array,add))//输出数组每个元素相加的结果
print(arrayAdd(array,multiply)) //输出数组每个元素相乘的结果
//在swift中运算符也是一种函数,所以还可以写成这样
print(arrayAdd(array,*))

8.函数的递归调用
要使用递归函数,先要想好以下几点
1.递归公式
2.收敛条件
3.循环能够容易解决的不用递归,递归效率太低

//以求n的阶层为例
func f (n:Int) -> Int{
    if n == 0 || n == 1{ //收敛条件,当n等于0或1的时候返回1
        return 1
    }
    //n的阶层等于n乘以n-1的阶层 以此类推,当调到2乘以1的阶层就算出了结果,再以此回推到n的阶层的值
    return n * f(n-1) 
}
print(f(3))

//汉诺伊塔
func hanoi(n:Int,a:String,b:String,c:String) {
    if n > 0{
       //要将n个圈从a -> b 需要将 n-1个圈从a->c
       hanoi(n-1, a: a, b: c, c: b)
       //最下面的一个从a->b
       print("\(a) -> \(b)")
       //再将c上面的n-1个移到b
       hanoi(n-1, a: c, b: b , c: a)
    }
}
hanoi(2, a: "A", b: "B", c: "C")

函数的闭包

函数的闭包(Closures),闭包在swift是一个匿名函数,跟c和oc中的Block,其他语言中的lambda表达式非常相似。在代码的终极规则里面,始终要围绕高内聚,低耦合的原则,而函数的闭包就是为了降低代码的耦合性而存在的。

var array = [12,35,23,56,45]
func arrayAdd (array:[Int],_ fn:(Int,Int) -> Int){
    var sum = array[0]
    for i in array[1..<array.count]{
        sum = fn(sum,i) //通过传入的函数可以做任意的二元运算
    }
    return sum
}
//函数的参数为函数类型,就可以写一个匿名函数当做参数直接传入,这就称为函数的闭包
arrayAdd(array,fn:{(a:Int,b:Int) -> Int in
     return a+b
}
//函数类型的参数为最后一个参数,还可以写成尾随闭包
arrayAdd(array){(a:Int,b:Int) -> Int in
     return a+b
}
//根据swift的类型推断还可以写成
arrayAdd(array){(a,b) ->Int in
     return a+b
}
//还可以写成极具逼格的样子
arrayAdd(array){$0+$1} //$0代表第一个参数,$1代表第二个参数

数组中非常重要的三个方法,它们都用到了函数的闭包概念

let array = [12,45,32,78,25,18]
//1.过滤 关键字filter
//{$0>30}就是尾随闭包,因为该方法只有一个闭包参数,所以圆括号都省略了
let newArray = array.filter{$0>30}//过滤条件就是元素值必须要大于30
print(newArray) //结果[45,32,78]
//2.映射 关键字map
let newArray1 = array.map{$0+2}
print(newArray1) //结果[14,47,34,80,27,20]
//3.缩减 关键字reduce
//获取数组的最大值 array[0]是初始值,{}是闭包函数,函数内比较元素之间大小,返回大的值
let newArray3 = array.reduce(array[0]) {$0 > $1 ? $0:$1}
print(newArray3)

类是对象的蓝图和模板,为对象提供了属性和方法。那么什么是对象呢,举个例子,比如我的杯子就是一个对象,它有颜色、容量、品牌,这就是我的杯子这个对象的属性,它还能装水,这就对象的行为,而我的杯子它也是属于杯子这一类。所以总的来说一切皆为对象,而且对象都有属性和行为,对象都属于某个类,并且每个对象都是独一无二的。而且类是抽象的,对象是具体的。
下面先来看看怎么定义一个类的:
1.首先对类数据抽象
2.行为抽象
3.初始化方法,也叫构造器 创建对象的时候调用的方法

class Student{
    //变量定义到类的外面就叫变量 - variable 变量定义到类的里面叫做属性 - property
    //数据抽象 - 找到和学生相关的属性(找名词)
    var name:String
    var age:Int
    
    //初始化方法(构造方法/构造器) - 创建对象要使用的方法 constructor
    init(name:String,age:Int){
        self.name = name
        self.age = age
    }
    
    //函数写到类的外面叫函数(function) 函数写在类的里面叫方法(method)
    //行为抽象 - 找到和学生相关的方法(找动词)
    func eat() {
        print("\(name)正在吃饭")
    }
    func study(courseName:String){
        print("\(name)正在学习\(courseName)")
    }
    func watchJapaneseAV(){
        if age >= 18{
            print("\(name)正在观看岛国爱情动作片")
        }
        else{
            print("\(name)年龄不够,过两年再来")
        }
    }
}
//创建对象,调用初始化方法,传入值
var student1 = Student(name: "刘琼", age: 20)
//给对象发消息,让它执行相关的操作
student1.watchJapaneseAV()
student1.study("Swift程序设计")

面向对象编程:

上面一段代码我们就已经完成了面向对象编程的操作,面向对象的编程步骤其实就可以分为以下三步:
1.定义类(如果你要使用的类苹果已经提供直接跳过该步骤)
2.创建对象(调用初始化方法)
3.给对象发消息(最终目标是解决问题)

类的便利初始化方法

1.一个类中可以存在多个初始化方法
2.利用关键字convenience可以定义一个便利初始化方法,这个方法调用的是其他初始化方法
3.创建对象的时候就可以选择调用需要的初始化方法

//定义一个点的类
class Point{
    var x:Double
    var y:Double
    //可以在一个类中定义多个初始化方法(初始化方法的重载)
    
    //便利初始化方法/便利构造器
    //调用了其他的初始化方法的初始化方法
    convenience init(){
        self.init(x:0,y:0)
    }
    //指派初始化方法 /指派构造器
    init (x:Double,y:Double){
        self.x = x
        self.y = y
    }
    
    func cul(other:Point) -> Double{
        return sqrt((x-other.x)*(x-other.x)+(y-other.y)*(y-other.y))
    }
    func move(a:Double,b:Double){
        x = a
        y = b
    }
}
let point1 = Point()//调用便利初始化方法
let point2 = Point(x:1,y:2)//调用的指派初始化方法

类的计算属性

在类里面经常会有一种调用属性计算出来的值,这时候我们可以不把它定义成一个方法,直接用计算属性来定义就行了,例如下面计算矩形面积和周长:

class Rectangle{
    var height:Int
    var width:Int
    init(height:Int,width:Int){
        //self用来区分传进来的值和类的属性,名字一样才需要加
        self.height = height 
        self.width = width
    }
    //计算周长用到的全是类里面的属性,所以可以写成计算变量
    var permiter:Int{
        //get设置只读,矩形的周长是不能直接改变的,
        //set设置可访问,就可以修改该属性
        get{return 2*(height+width)} 
    }
}
let rect = Rectangle(height:10,width:20)
print(rect.permiter) //直接获取计算属性的值

类的修饰符

在定义类的时候我们可以在class前面加一个修饰符,实现不同的访问约束。
1.public (公共的)在项目外,只要将类拷贝过去也可以实现访问。
2.internal (内部的) 不加修饰符的时候默认为internal,只能在项目内实现访问
3.private (私有的) 只能够在类的里面进行访问,一般用来保护类里面数据的安全性

一般使用修饰符都应该遵循以下几点:
方法是公开的,数据是私有的

  1. 存储属性一般是Private 因为数据是要保护起来的
  2. 方法一般是public的,因为方法是对象接受的消息
  3. 如果自定义的类没有打算在其他项目中使用,可以不写访问修饰符
  4. 直接使用默认的internal修饰符,表示在本项目中是公开对其他项目私有
//定义一个公开的类
public class Circle{
    //将存储属性用private保护起来,外界不能直接访问
    //定义私有存储属性,命名一般以下划线开始
    private var _center:Point 
    private var _radius:Double
    
    //用计算属性让外界读取
    var center:Point{
        get{return _center}  //只读
    }
    var radius:Double{
        get{return _radius}
    }
    
    init(center:Point,radius:Double){
        _center = center
        _radius = radius
    }
    //方法公开,这里面积最好写成计算属性,只是为了方便举例所以用了方法。
    public func area() -> {
        return M_PI*_radius*_radius
    }
}

类的扩展

在某种情况下,某个特定的应用场景中你发现现有的类缺少某项功能,这时候要么重新写一个类来实现现在需要的功能,但是这样就显得太麻烦了,在swift中有一种类的扩展功能,我们可以对已有的类进行扩展,添加这项功能:

//扩展Point,让它调用cgPoint能够直接转化成CGPoint  
//可以通过类扩展(extension)的方式添加这项功能
extension Point{
    var cgPoint:CGPoint{
        get{
            return CGPointMake(CGFloat(x), CGFloat(y))
        }
    }
}
//很多系统自带的类也可以拓展
//扩展UIColor类,生成随机色
//randomInt()是我自定义的一个随机生成整数的函数
extension UIColor{
    static func randomColor() -> UIColor{
        let red = CGFloat(randomInt(0, 255))/255.0
        let green = CGFloat(randomInt(0, 255))/255.0
        let blue = CGFloat(randomInt(0, 255))/255.0
        return UIColor(red: red, green: green, blue: blue, alpha: 1)
    }
}

运算符重载和级联式编程

在swift中所有的运算符都是一个函数,内置运算符能够运算的类型太局限,如果我们自己定义的某个类型也需要用到运算符运算,这时候就需要对运算符进行重载,来实现自定义类型的运算。

级联式编程是定义的方法是返回处理过后的本身,然后多个方法都是这样的,可以同时调用,形成一个链状的代码,也称为开火车式编程。

class Fration{
    //私有化存储变量
    private var _num:Double
    private var _den:Double
    //初始化
    init(num:Double,den:Double){
        _num = num 
        _den = den
        normalize()
        simplify()
    }
    //用来显示分数的计算属性
     var info:String{
        get{

            return  _num == 0 || _den == 1 ? "\(_num)":"\(_num)/\(_den)"
        }
    }
    //读取分子分母的计算属性
    var num:Int{
        get{return _num}
    }
    var den:Int{
        get{return _den}
    }
    //定义分数的加减乘数方法
    func add(a:Fraction) -> Fraction{
        let num1 = _num*a.den+a.num*_den
        let den1 = _den*a.den
        //这样的编程模式为级联式编程
        //每次调用的方法返回的都是处理过后的本身
        return Fraction(num: num1, den: den1).simplify().normalize()
    }
    func cut(a:Fraction) -> Fraction{
        let num1 = _num*a.den-a.num*_den
        let den1 = _den*a.den
        return Fraction(num: num1, den: den1)
    }
    func mul(a:Fraction) -> Fraction{
        let num1 = _num*a.num
        let den1 = _den*a.den
        return Fraction(num: num1, den: den1)
    }
    func div(a:Fraction) -> Fraction{
        let num1 = _num*a.den
        let den1 = _den*a.num
        return Fraction(num: num1, den: den1)
    }
    //定义约分的方法
    func simplify() -> Fraction{
        if _num == 0{
            _den = 1
        }
        else{
            //gcd是自定义求两个数的最大公约数的函数
            let gcd1 = gcd(abs(_num),abs(_den))
            _num = _num/gcd1
            _den = _den/gcd1
        }
        return self
    }
    //定义分数正常化得方法
    func normalize() -> Fraction{
        //如果分数小于0,分子分母分别取负值
        if _den < 0{
            _num = -_num
            _den = -_den
        }
        //返回self之后可以直接在Fraction类型后面.出该方法。
        return self
    }

}
//其实这样我们已经完成了分数类的运算,但是我想它更完美,能像整形这些类型一样使用运算符
//这样就要用到运算符的重载
func + (one:Fraction,two:Fraction) -> Fraction{
    return one.add(two)
}
//重载完成之后就可以直接通过运算符来对我定义的类型运算
let a = Fraction(num: 3, den: -2)
let b = Fraction(num: -3, den: 4)
print("\(a.info)+\(b.info)=\((a+b).info)") 
//这样就实现了自定义类型的分数的运算符相加

类的文档注释

一个好的程序员都拥有写注释的习惯,这样不仅能够让别人看懂你写的代码,还能让以后的你看懂自己以前写的代码,文档注释能够在其它调用的地方通过alt来查看方法说明和参数说明,使别人一眼就能够看懂你这方法和类是干嘛的。还是以学生类为例:

/// 学生类
public class Student{
    //变量定义到类的外面就叫变量 - variable 变量定义到类的里面叫做属性 - property
    //数据抽象 - 找到和学生相关的属性(找名词)
    private var _name:String
    private var _age:Int
    
    //初始化方法(构造方法/构造器) - 创建对象要使用的方法 constructor
    public init(name:String,age:Int){
        _name = name
        _age = age
    }
    
    var name:String{
        get{
            //前进几个字符
            let displayName = _name.substringToIndex(_name.endIndex.advancedBy(-1))
            //let displayName = _name.substringToIndex(_name.endIndex.predecessor())//取1-倒数第二个
            return displayName + "*"}
    }
    var age:Int{
        get{return _age}
    }
    /**
     吃饭
     */
    public func eat() {
        print("\(_name)正在吃饭")
    }
    /**
     学习
     
     - parameter courseName: 课程名称
     - parameter hour: 学习时间
     
     - returns: 学会了返回true 没有会返回false
     */
    public func study(courseName:String){
        print("\(name)正在学习\(courseName)")
    }
    
    /**
     看片
     - parameter name:片名,主演
     - returns: 学会了动作返回true
     */
    public func watchJapaneseAV(){
        if _age >= 18{
            print("\(name)正在观看岛国爱情动作片")
        }
        else{
            print("\(name)年龄不够,过两年再来")
        }
    }
}
//注释的结构 
///类名
/**
 方法说明
 -parameter 参数名:参数说明
 -returns 返回值说明
 */

类的继承

从已有的类里面创建一个新的类的过程叫做继承。提供继承信息的叫做父类(超类/基类),得到继承信息的为子类(派生类/衍生类),通常子类除了得到父类的继承信息还会增加一些自己特有的东西,所以子类的功能一定比父类更强大,继承的意义在于子类可以复用父类的代码并且增强系统现有的功能。

//1.首先定义父类
//2.在定义子类继承父类
//3.子类拥有一切属于父类的属性和方法
//4.子类增加自己特有的属性和方法
/**
 性别枚举
 
 -Boy 男
 -Girl 女
 */
enum Sex{
    case Boy,Girl
}
///人类
class Person {
    var name:String
    var age:Int
    var sex:Sex
    
    init(name:String,age:Int,sex:Sex){
        self.age = age
        self.name = name
        self.sex = sex
    }
    /**
     吃饭
     */
    func eat(){
        print("\(name)正在吃饭")
    }
   
}
///学生
class Student:Person { //继承人的类
    var major:String
    
    init(name:String,age:Int,major:String,sex:Sex){
        self.major = major //先初始化自己特有的方法
        super.init(name: name, age: age, sex:sex)
    }
    /**
     学习
     - parameter courseName: 课程名称
     */
    func study(courseName:String) {
        print("\(name)是\(major)专业的学生")
        print("\(sex == .Boy ? "他":"她")正在学习\(courseName)")
    }
}
//可以将子类型的对象赋值给父类型的变量(因为子类跟父类之间是IS-A关系)
//学生是人,老师是人,所以学生和老师的对象可以赋值给人类型的变量

let stu1:Person = Student(name: "王大屌", age: 20, major: "地信", sex: Sex.Boy)

//如果要讲父类型的变量处理成子类型的时候,需要用as运算符进行类型转换
//如果能够确认,父类型的变量中就是某种子类型的对象可以用 as! 进行转换
//如果不确定父类型的变量中是哪种子类型,用if as? 尝试转化
if let temp = stu1 as? Student{
    temp.study("卖钩子")
}

类的多态

同样的对象类型(Pet类型),接受同样的消息(调用相同的方法),但是做了不同的事情 这就是多态(polymorphism)。实现多态的关键技术:1.方法重现(子类在继承父类的过程对父类已有的方法进行重写而且不同的子类给出各自不同的版本)2.对象造型(将子类型当做父类型来使用)

//继承了Prt类,
class Cat: Pet {
    var hairColor:String?

    func catchTheMouse(){
        print("\(nickName)正在抓老鼠")
    }
    // 父类有的子类可以重新实现 这个过程叫方法重写
    // 需要在方法前添加override关键字
    // 重写有时也被称为置换、覆盖、覆写
    override func shout() {
        print("\(nickName)喵喵喵......")
    }
    override func paly() {
        super.paly()
        print("\(nickName)玩毛线")
    }
}
class Chicken: Pet {

    override func shout() {
        if gender == .Male{
            print("喔喔喔")
        }
        else{
            print("咯咯咯")
        }
    }
    override func eat() {
        print("\(nickName)在吃小虫")
    }
}
//上面两个类都继承了Pet类,并且都重写了父类中shout()方法
let pets = [Cat(nickName:"加菲",gender: .Female,age:3),
            Chicken(nickName: "傻鸡", gender: .Male, age: 1)]
for pet in pets{
//结果是两者给同一种类型发了同样的消息,但是结果却不同,这就是类的多态。
    pet.shout() 
//如果要调用子类型特有的方法需要将父类型转换成子类型
//在这就可以用if + as?安全转换
    if let pet = pet as? Cat{
        pet.catchTheMouse()
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容