#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()
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容