函數(shù)
函數(shù)是一種獨立的可以重復(fù)使用的功能模塊。是解決重復(fù)代碼的一種很有效的方法,下面直接來介紹swift的函數(shù):
1.常量參數(shù)
// func 關(guān)鍵字 sayHello 函數(shù)名 () 參數(shù) {}函數(shù)體,執(zhí)行操作的代碼
func sayHello(name:String) -> String{ //-> String為返回類型
return "hello,\(name)" //如果返回類型不為Void,必須返回與類型匹配的值
}
//參數(shù)默認是常量,不能在函數(shù)體內(nèi)修改 name:String 相當于let name:String
func sayHello(var name:String) -> String{
//參數(shù)也可以設(shè)置為變量,但這種方式是不好的,在swift3.0中要去除這種語法
name = "你"
}
2.參數(shù)的默認值
//在定義函數(shù)的時候,可以給參數(shù)設(shè)置一個默認值
func sum(a:Int=0,b:Int=0,c:Int=0) -> Int{
let total = a + b + c
return total
}
//有默認值的參數(shù)可以不傳值
print(sum(1)) //結(jié)果就是1,傳入1給參數(shù)a賦值。相當于1+0+0
3.參數(shù)列表(傳入可變參數(shù)個數(shù))
//參數(shù)為一個列表,參數(shù)的個數(shù)可以變化
func sum (nums:Int...) -> Int{
var total = 0
for num in nums{
total += num
}
return total
}
//調(diào)用的時候可以傳入任意個參數(shù)
print(sum(1,2,3,5,6,7))
4.外部參數(shù)名和內(nèi)部參數(shù)名
//y:Int 其實是省略了外部參數(shù)名,所以內(nèi)部參數(shù)名和外部參數(shù)名默認一樣
//相當于(x:Int,y y:Int的寫法)
func sum (x:Int,y:Int) -> Int{
return x+y
}
//調(diào)用的時候第一個參數(shù)默認不需要寫參數(shù)名,第二個以后每個都需要寫參數(shù)名
print(sum(5,y:3))
//如果需要讓調(diào)用的時候不用寫第二個參數(shù)名也可以寫(x:Int,_ y:Int)
//_ 下劃線表示省略外部參數(shù)名
func sum1 (x:Int,_ y:Int) -> Int{
return x+y
}
print(sum(5,3))
5.函數(shù)返回多個值
let array = [1,5,7,9,6]
//如果需要函數(shù)返回多個值,可以用元組來進行返回
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) //結(jié)果為1
6.函數(shù)輸入輸出參數(shù)
//通過inout才能改變函數(shù)外部的值
//inout - 輸入輸出參數(shù)(不僅將數(shù)據(jù)傳入函數(shù)還要從函數(shù)中取出數(shù)據(jù))
func swap(inout a:Int,inout b:Int) -> Void{
(a,b)=(b,a)
}
var x = 5
var y = 10
swap(&x, b:&y) //調(diào)用的時候參數(shù)的值一定要加&
print(x,y)
7.函數(shù)類型的參數(shù)
//定義了一個兩個數(shù)相加的函數(shù)和一個相乘的函數(shù)
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]
//該函數(shù)的第二個參數(shù)就是函數(shù)類型
func arrayAdd (array:[Int],_ fn:(Int,Int) -> Int){
var sum = array[0]
for i in array[1..<array.count]{
sum = fn(sum,i) //通過傳入的函數(shù)可以做任意的二元運算
}
return sum
}
print(arrayAdd(array,add))//輸出數(shù)組每個元素相加的結(jié)果
print(arrayAdd(array,multiply)) //輸出數(shù)組每個元素相乘的結(jié)果
//在swift中運算符也是一種函數(shù),所以還可以寫成這樣
print(arrayAdd(array,*))
8.函數(shù)的遞歸調(diào)用
要使用遞歸函數(shù),先要想好以下幾點
1.遞歸公式
2.收斂條件
3.循環(huán)能夠容易解決的不用遞歸,遞歸效率太低
//以求n的階層為例
func f (n:Int) -> Int{
if n == 0 || n == 1{ //收斂條件,當n等于0或1的時候返回1
return 1
}
//n的階層等于n乘以n-1的階層 以此類推,當調(diào)到2乘以1的階層就算出了結(jié)果,再以此回推到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")
函數(shù)的閉包
函數(shù)的閉包(Closures),閉包在swift是一個匿名函數(shù),跟c和oc中的Block,其他語言中的lambda表達式非常相似。在代碼的終極規(guī)則里面,始終要圍繞高內(nèi)聚,低耦合的原則,而函數(shù)的閉包就是為了降低代碼的耦合性而存在的。
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) //通過傳入的函數(shù)可以做任意的二元運算
}
return sum
}
//函數(shù)的參數(shù)為函數(shù)類型,就可以寫一個匿名函數(shù)當做參數(shù)直接傳入,這就稱為函數(shù)的閉包
arrayAdd(array,fn:{(a:Int,b:Int) -> Int in
return a+b
}
//函數(shù)類型的參數(shù)為最后一個參數(shù),還可以寫成尾隨閉包
arrayAdd(array){(a:Int,b:Int) -> Int in
return a+b
}
//根據(jù)swift的類型推斷還可以寫成
arrayAdd(array){(a,b) ->Int in
return a+b
}
//還可以寫成極具逼格的樣子
arrayAdd(array){$0+$1} //$0代表第一個參數(shù),$1代表第二個參數(shù)
數(shù)組中非常重要的三個方法,它們都用到了函數(shù)的閉包概念
let array = [12,45,32,78,25,18]
//1.過濾 關(guān)鍵字filter
//{$0>30}就是尾隨閉包,因為該方法只有一個閉包參數(shù),所以圓括號都省略了
let newArray = array.filter{$0>30}//過濾條件就是元素值必須要大于30
print(newArray) //結(jié)果[45,32,78]
//2.映射 關(guān)鍵字map
let newArray1 = array.map{$0+2}
print(newArray1) //結(jié)果[14,47,34,80,27,20]
//3.縮減 關(guān)鍵字reduce
//獲取數(shù)組的最大值 array[0]是初始值,{}是閉包函數(shù),函數(shù)內(nèi)比較元素之間大小,返回大的值
let newArray3 = array.reduce(array[0]) {$0 > $1 ? $0:$1}
print(newArray3)
類
類是對象的藍圖和模板,為對象提供了屬性和方法。那么什么是對象呢,舉個例子,比如我的杯子就是一個對象,它有顏色、容量、品牌,這就是我的杯子這個對象的屬性,它還能裝水,這就對象的行為,而我的杯子它也是屬于杯子這一類。所以總的來說一切皆為對象,而且對象都有屬性和行為,對象都屬于某個類,并且每個對象都是獨一無二的。而且類是抽象的,對象是具體的。
下面先來看看怎么定義一個類的:
1.首先對類數(shù)據(jù)抽象
2.行為抽象
3.初始化方法,也叫構(gòu)造器 創(chuàng)建對象的時候調(diào)用的方法
class Student{
//變量定義到類的外面就叫變量 - variable 變量定義到類的里面叫做屬性 - property
//數(shù)據(jù)抽象 - 找到和學(xué)生相關(guān)的屬性(找名詞)
var name:String
var age:Int
//初始化方法(構(gòu)造方法/構(gòu)造器) - 創(chuàng)建對象要使用的方法 constructor
init(name:String,age:Int){
self.name = name
self.age = age
}
//函數(shù)寫到類的外面叫函數(shù)(function) 函數(shù)寫在類的里面叫方法(method)
//行為抽象 - 找到和學(xué)生相關(guān)的方法(找動詞)
func eat() {
print("\(name)正在吃飯")
}
func study(courseName:String){
print("\(name)正在學(xué)習(xí)\(courseName)")
}
func watchJapaneseAV(){
if age >= 18{
print("\(name)正在觀看島國愛情動作片")
}
else{
print("\(name)年齡不夠,過兩年再來")
}
}
}
//創(chuàng)建對象,調(diào)用初始化方法,傳入值
var student1 = Student(name: "劉瓊", age: 20)
//給對象發(fā)消息,讓它執(zhí)行相關(guān)的操作
student1.watchJapaneseAV()
student1.study("Swift程序設(shè)計")
面向?qū)ο缶幊蹋?/h3>
上面一段代碼我們就已經(jīng)完成了面向?qū)ο缶幊痰牟僮鳎嫦驅(qū)ο蟮木幊滩襟E其實就可以分為以下三步:
1.定義類(如果你要使用的類蘋果已經(jīng)提供直接跳過該步驟)
2.創(chuàng)建對象(調(diào)用初始化方法)
3.給對象發(fā)消息(最終目標是解決問題)
類的便利初始化方法
1.一個類中可以存在多個初始化方法
2.利用關(guān)鍵字convenience可以定義一個便利初始化方法,這個方法調(diào)用的是其他初始化方法
3.創(chuàng)建對象的時候就可以選擇調(diào)用需要的初始化方法
//定義一個點的類
class Point{
var x:Double
var y:Double
//可以在一個類中定義多個初始化方法(初始化方法的重載)
//便利初始化方法/便利構(gòu)造器
//調(diào)用了其他的初始化方法的初始化方法
convenience init(){
self.init(x:0,y:0)
}
//指派初始化方法 /指派構(gòu)造器
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()//調(diào)用便利初始化方法
let point2 = Point(x:1,y:2)//調(diào)用的指派初始化方法
類的計算屬性
在類里面經(jīng)常會有一種調(diào)用屬性計算出來的值,這時候我們可以不把它定義成一個方法,直接用計算屬性來定義就行了,例如下面計算矩形面積和周長:
class Rectangle{
var height:Int
var width:Int
init(height:Int,width:Int){
//self用來區(qū)分傳進來的值和類的屬性,名字一樣才需要加
self.height = height
self.width = width
}
//計算周長用到的全是類里面的屬性,所以可以寫成計算變量
var permiter:Int{
//get設(shè)置只讀,矩形的周長是不能直接改變的,
//set設(shè)置可訪問,就可以修改該屬性
get{return 2*(height+width)}
}
}
let rect = Rectangle(height:10,width:20)
print(rect.permiter) //直接獲取計算屬性的值
類的修飾符
在定義類的時候我們可以在class前面加一個修飾符,實現(xiàn)不同的訪問約束。
1.public (公共的)在項目外,只要將類拷貝過去也可以實現(xiàn)訪問。
2.internal (內(nèi)部的) 不加修飾符的時候默認為internal,只能在項目內(nèi)實現(xiàn)訪問
3.private (私有的) 只能夠在類的里面進行訪問,一般用來保護類里面數(shù)據(jù)的安全性
一般使用修飾符都應(yīng)該遵循以下幾點:
方法是公開的,數(shù)據(jù)是私有的
- 存儲屬性一般是Private 因為數(shù)據(jù)是要保護起來的
- 方法一般是public的,因為方法是對象接受的消息
- 如果自定義的類沒有打算在其他項目中使用,可以不寫訪問修飾符
- 直接使用默認的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
}
}
類的擴展
在某種情況下,某個特定的應(yīng)用場景中你發(fā)現(xiàn)現(xiàn)有的類缺少某項功能,這時候要么重新寫一個類來實現(xiàn)現(xiàn)在需要的功能,但是這樣就顯得太麻煩了,在swift中有一種類的擴展功能,我們可以對已有的類進行擴展,添加這項功能:
//擴展Point,讓它調(diào)用cgPoint能夠直接轉(zhuǎn)化成CGPoint
//可以通過類擴展(extension)的方式添加這項功能
extension Point{
var cgPoint:CGPoint{
get{
return CGPointMake(CGFloat(x), CGFloat(y))
}
}
}
//很多系統(tǒng)自帶的類也可以拓展
//擴展UIColor類,生成隨機色
//randomInt()是我自定義的一個隨機生成整數(shù)的函數(shù)
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)
}
}
運算符重載和級聯(lián)式編程
在swift中所有的運算符都是一個函數(shù),內(nèi)置運算符能夠運算的類型太局限,如果我們自己定義的某個類型也需要用到運算符運算,這時候就需要對運算符進行重載,來實現(xiàn)自定義類型的運算。
級聯(lián)式編程是定義的方法是返回處理過后的本身,然后多個方法都是這樣的,可以同時調(diào)用,形成一個鏈狀的代碼,也稱為開火車式編程。
class Fration{
//私有化存儲變量
private var _num:Double
private var _den:Double
//初始化
init(num:Double,den:Double){
_num = num
_den = den
normalize()
simplify()
}
//用來顯示分數(shù)的計算屬性
var info:String{
get{
return _num == 0 || _den == 1 ? "\(_num)":"\(_num)/\(_den)"
}
}
//讀取分子分母的計算屬性
var num:Int{
get{return _num}
}
var den:Int{
get{return _den}
}
//定義分數(shù)的加減乘數(shù)方法
func add(a:Fraction) -> Fraction{
let num1 = _num*a.den+a.num*_den
let den1 = _den*a.den
//這樣的編程模式為級聯(lián)式編程
//每次調(diào)用的方法返回的都是處理過后的本身
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是自定義求兩個數(shù)的最大公約數(shù)的函數(shù)
let gcd1 = gcd(abs(_num),abs(_den))
_num = _num/gcd1
_den = _den/gcd1
}
return self
}
//定義分數(shù)正常化得方法
func normalize() -> Fraction{
//如果分數(shù)小于0,分子分母分別取負值
if _den < 0{
_num = -_num
_den = -_den
}
//返回self之后可以直接在Fraction類型后面.出該方法。
return self
}
}
//其實這樣我們已經(jīng)完成了分數(shù)類的運算,但是我想它更完美,能像整形這些類型一樣使用運算符
//這樣就要用到運算符的重載
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)")
//這樣就實現(xiàn)了自定義類型的分數(shù)的運算符相加
類的文檔注釋
一個好的程序員都擁有寫注釋的習(xí)慣,這樣不僅能夠讓別人看懂你寫的代碼,還能讓以后的你看懂自己以前寫的代碼,文檔注釋能夠在其它調(diào)用的地方通過alt來查看方法說明和參數(shù)說明,使別人一眼就能夠看懂你這方法和類是干嘛的。還是以學(xué)生類為例:
/// 學(xué)生類
public class Student{
//變量定義到類的外面就叫變量 - variable 變量定義到類的里面叫做屬性 - property
//數(shù)據(jù)抽象 - 找到和學(xué)生相關(guān)的屬性(找名詞)
private var _name:String
private var _age:Int
//初始化方法(構(gòu)造方法/構(gòu)造器) - 創(chuàng)建對象要使用的方法 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-倒數(shù)第二個
return displayName + "*"}
}
var age:Int{
get{return _age}
}
/**
吃飯
*/
public func eat() {
print("\(_name)正在吃飯")
}
/**
學(xué)習(xí)
- parameter courseName: 課程名稱
- parameter hour: 學(xué)習(xí)時間
- returns: 學(xué)會了返回true 沒有會返回false
*/
public func study(courseName:String){
print("\(name)正在學(xué)習(xí)\(courseName)")
}
/**
看片
- parameter name:片名,主演
- returns: 學(xué)會了動作返回true
*/
public func watchJapaneseAV(){
if _age >= 18{
print("\(name)正在觀看島國愛情動作片")
}
else{
print("\(name)年齡不夠,過兩年再來")
}
}
}
//注釋的結(jié)構(gòu)
///類名
/**
方法說明
-parameter 參數(shù)名:參數(shù)說明
-returns 返回值說明
*/
類的繼承
從已有的類里面創(chuàng)建一個新的類的過程叫做繼承。提供繼承信息的叫做父類(超類/基類),得到繼承信息的為子類(派生類/衍生類),通常子類除了得到父類的繼承信息還會增加一些自己特有的東西,所以子類的功能一定比父類更強大,繼承的意義在于子類可以復(fù)用父類的代碼并且增強系統(tǒng)現(xiàn)有的功能。
//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)正在吃飯")
}
}
///學(xué)生
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)
}
/**
學(xué)習(xí)
- parameter courseName: 課程名稱
*/
func study(courseName:String) {
print("\(name)是\(major)專業(yè)的學(xué)生")
print("\(sex == .Boy ? "他":"她")正在學(xué)習(xí)\(courseName)")
}
}
//可以將子類型的對象賦值給父類型的變量(因為子類跟父類之間是IS-A關(guān)系)
//學(xué)生是人,老師是人,所以學(xué)生和老師的對象可以賦值給人類型的變量
let stu1:Person = Student(name: "王大屌", age: 20, major: "地信", sex: Sex.Boy)
//如果要講父類型的變量處理成子類型的時候,需要用as運算符進行類型轉(zhuǎn)換
//如果能夠確認,父類型的變量中就是某種子類型的對象可以用 as! 進行轉(zhuǎn)換
//如果不確定父類型的變量中是哪種子類型,用if as? 嘗試轉(zhuǎn)化
if let temp = stu1 as? Student{
temp.study("賣鉤子")
}
類的多態(tài)
同樣的對象類型(Pet類型),接受同樣的消息(調(diào)用相同的方法),但是做了不同的事情 這就是多態(tài)(polymorphism)。實現(xiàn)多態(tài)的關(guān)鍵技術(shù):1.方法重現(xiàn)(子類在繼承父類的過程對父類已有的方法進行重寫而且不同的子類給出各自不同的版本)2.對象造型(將子類型當做父類型來使用)
//繼承了Prt類,
class Cat: Pet {
var hairColor:String?
func catchTheMouse(){
print("\(nickName)正在抓老鼠")
}
// 父類有的子類可以重新實現(xiàn) 這個過程叫方法重寫
// 需要在方法前添加override關(guān)鍵字
// 重寫有時也被稱為置換、覆蓋、覆寫
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{
//結(jié)果是兩者給同一種類型發(fā)了同樣的消息,但是結(jié)果卻不同,這就是類的多態(tài)。
pet.shout()
//如果要調(diào)用子類型特有的方法需要將父類型轉(zhuǎn)換成子類型
//在這就可以用if + as?安全轉(zhuǎn)換
if let pet = pet as? Cat{
pet.catchTheMouse()
}
}