swift語法
swift初見
第一個程序輸出hello,world!
print("hello,word!")
在swift中這行代碼就算一個完整的程序。你不需要寫一個main函數,因為在swift中左邊的文件main.swift就是程序的入口,所以swift中的這個文件的文件名必須是main,否則會報錯。如果將文件名改為其他名字,程序就找不到入口而報錯
在swift中也不需要寫分號,每一個換行就代表一句話的結束(注意:上面的打印語句是沒有寫分號的)。但如果兩句話寫在一行則需要寫分號:
print("hello,");print("world!")
第一章:常量和變量
常量和變量的聲明
使用let來聲明常量,使用var來聲明變量。一個常量的值,在編譯的時候并不需要有明確的值,但是你只能為它賦值一次。swift中盡量使用常量,因為系統會對常量進行優化,而變量則不會
let constant1: Int
let bconstant2 = 20
var variable = 30.5
variable = 50
常量和變量的類型和你賦給它的值一樣。然而聲明時類型是可選的,聲明的同時賦值的話,編譯器會自動推斷類型。在上面的例子中,編譯器推斷出==constant2==是一個整數,==variable==是一個浮點數。如果初始值沒有提供足夠信息(或者沒有初始值),那么久需要在變量或者常量后面聲明類型,用冒分割,正如==constant1== 。
作用域:
變量和常量只在使用它的花括號里起作用,作用域越小越好,能用常量的地方就不要使用變量(編譯器會優化常量,使代碼效率更高)
類型轉換
swift 中沒有隱式轉換,如果需要把一個值轉換為其他類型,必須顯示轉換,例如
let a = 5
//必須將 a轉化為浮點型才能與5.5相加,否則會報錯
let b = (float)a + 5.5
將數字轉換為字符串
let name = "王大錘"
let age = 25
let information = name + string(age) + "歲"
最后會輸出”王大錘25歲"
其實還有一種把值轉換成字符串的方法:把值寫到括號中去,并且在值的前面加反斜杠==(\ )==
例如:
let apples = 5
let appleSumary = "我有(\apples)個蘋果"
常見的類型
swift中常見的類型有整型、浮點型、字符串
整型分為有符號整型和無符號整型
在64位中,有符號整型表示的范圍是 - 2^32 ~ 2^32 - 1 ;無符號整型所能表示的范圍是0 ~ 2^64 - 1
第二章:運算符
認識基本運算符
運算符有一元、二元和三元運算符:
一元運算符對單一操作對象操作(如-a)。一元運算符需緊排操作對象之前(如!b)
二元運算符操作兩個操作對象(如2 + 3),是中置的,因為它們出現在兩個操作對象之間。
三元運算符操作三個操作對象,和 C 語言一樣,Swift 只有一個三元運算符,就是三元條件運算符(a ? b : c)。
常見的運算符有+, -, *, /, %, *, =, +=, -=, *=, /=, ==, >, <, >=, <=, ..., ||, &&, !
swift中舍棄了自增 ++ 和自減 --
swift中的運算符其實都是函數
1.賦值運算符 : = += -= /= = %=
2.算數運算符 :+ - / %
關系運算符 :== != < <= > >=
邏輯運算符 :&& || !
三元條件運算符: ? :
其他運算符: 點運算. 下標運算[] 可空類型? 保證有值 ! 其他??
可空類型 ?? other :意思是如果前面的可空類型是空,那么久執行后面代碼,如果前面的可空類型有值,就執行前面的可空類型
易錯知識點:
1.浮點數求余運算:
swiftz中可以對浮點數進行求余運算。
例如:9.0 除 2.5等于 3余 1.5,所以結果是一個Double值1.5
2.三元條件運算(Ternary Conditional Operator)
三元條件運算的特殊在于它是有三個操作數的運算符,它的原型是: 問題?答案1:答案2。如果問題成立,返回答案1的結果; 如果不成立,返回答案2的結果。三元運算可以替換if - else語句,使代碼更加精簡明了
3.區間運算符
閉區間運算符(a...b)定義一個包含從a到b(包括a和b)的所有值的閉區間。c常在for-in循環中使用
第三章:流程控制
使用==if==和switch來進行條件操作,使用==for - in==、==while==、==repeat - while==來進行循環。其中條件和循環變量的括號可以省略
if語句的使用示例
1、隨便寫一個年分判斷是否是閏年
let year = 1999
if year % 4 == 0 && year % 100 != 0 || year % 400 == 0 {
print("\(year)是閏年")
}else{
print("\(year)不是閏年")
}
2、給出某年某月,打印該月的天數
let year = 2004
let mouth = 2
var days
if mouth = 2{
days = year % 4 == 0 && year % 100 != 0 || year % 4 == 0 ? 29: 28
}
else if mouth = 4 || mouth = 6 || mouth = 9 || mouth = 11 {
days = 30
}else{
days = 31
}
print(day)
同樣,switch語句(開關語句)同樣可以實現分之結構,我更傾向于把它理解為一個開關,它就像一個擁有多個閘門的開關一樣,選擇性的接通某個通道
switch使用示例
隨機生成一個整數,用switch判斷這個整數是什么
let number = Int(arc4random_uniform(3)) + 1
swith number {
case 1:
print("a")
case 2:
print("b")
default:
print(c)
}
以上為一個簡單的開關語句,在swift中以下幾點需要注意:
1、switch語句不能穿透,必須寫==default==
2、switch運行到匹配的句子之后,程序退出switch語句,并不會繼續往下執行,所以不需要在每個句子后寫==break== 。
for循環的使用示例
利用==for - in== 來進行循環操作
==示例1:==求1000以內的偶數和
let n = 1000
var result = 0
for i in 2...n{
if i % 2 == 0 {
result += i
}
}
==示例2:==經典的猴子吃桃問題
let days = 10
let result = 1
for _ in 1..<10{
result = 2 * (result + 1)
}
print(result)
在上述兩個示例中,for - in 的用法有兩處不同。如果不需要用循環次數做操作的時候,in 前面就可以用下劃線 _ 代替,并不會影響循環的執行。in 后面代表的是循環次數,== ... == 代表閉區間,== ..< == 代表左閉右開區間。
while循環的使用
使用while來重復運行一段代碼直到不滿足條件。循環條件既可以在開頭,也可以在結尾。repeat - while與c語言的do - while 用法相同,當程序執行一次之后再判斷是否滿足條件。swift 中do作為其他關鍵字,所以用repeat。
var n = 2
while n < 100 {
n = n * 2
}
print(n)
var m = 2
//do {
// m = m * 2
//} while m < 100
repeat {
m = m * 2
} while m < 100
print(m)
while循環的使用示例
示例1:打印1到100以內的素數
//第一步:找出素數
func isPrime(n: Int) -> Bool{
var i = 2
//在2~根號n之間找有沒有n的因子
while i <= Int(sqrt(Double(n))){
if n % i == 0{
return false
}
i += 1
}
return true
}
//第二步:打印素數
for n in 1...100{
if isprime(){
print(n)
}
}
示例二:找兩個數的最大公約數和最小公倍數
//最大公約數
func gcd (x: Int, y: Int) -> Int {
let i = x < y ? x: y
while{
if x % i ==0 && y % i == 0{
return i
}
i -= 1
}
return 1
}
//最小公倍數
func lcm (x: Int, y: Int) -> Int{
return x * y / gcd(x, y: y)
}
運用遞歸找最大公約數效率更高
// 短除法(歐幾里得算法)
// x和y的最大公約數跟y%x和x的最大公約數是一樣的
// 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
}
}
循環綜合練習
switch的嵌套使用
人機猜拳游戲
var computer = arc4random_uniform(3) + 1
var human: Int
repeat{
print("請出拳,(1剪刀,2石頭,3布)",terminator: "")
human = inputNum()
}while human < 1 || human > 3
switch computer{
case 1:
print("計算機出了剪刀")
switch human{
case 1:
print("平局")
case 2:
print("你贏了")
case 3:
print("你輸了")
default:
break
}
case 2:
print("計算機出了石頭")
switch human{
case 1:
print("輸了")
case 2:
print("平局")
case 3:
print("贏了")
default:
break
}
case 3:
print("計算機出了布")
switch human{
case 1:
print("贏了")
case 2:
print("輸了")
case 3:
print("平局")
default:
break
}
default:
break
}
for循環的嵌套使用
一:百錢買白雞問題(窮舉法:窮盡所有的可能性,找出正確答案)
問題描述: 公雞一只5錢,母雞一只3錢,小雞三只1錢,用一百錢買100只雞,算每種雞各多少只?
//公雞最多可以買20只,母雞最多買33只
for x in 0...20{
for y in o...33{
//小雞總數
let z = 100 - x - y
if z / 3 + x + y == 100 && x + y + z == 100{
print("公雞\(x)只,母雞\(y)只,小雞\(z)只")
}
}
}
for 與while 嵌套使用
二:五人分魚問題^
var total = 1
var isEnough = ture
while {
//當次循環時total作為魚的總數
for _ in 1...5{
if (total - 1) % 5 == 0{
//如果夠分,就扔掉一條再乘以五分之四作為下一次分魚的總數
total = (total - 1) / 5 * 4
}else{
//如果不夠分就直接退出for循環,讓初始總數加一
isEnough = false
break
}
}
if isEnough {
print(total)
break
}
total += 1
}
章節練習
練習題1:輸出九九乘法表
練習題2:雙骰子賭博游戲
練習題3:打印斐波拉契數列
第四章:數組
數組的創建與賦值
//創建數組的幾種方式
var array1 = [Int]()
var array2: [Int] = []
var array3 = [12, 23,34]
var array4 = [Int](count: 10, repeatedValue: 1)
var array5: [String] = []
var array6 = ["王", "大", "錘"]
//數組的賦值
var array7 = array6 //可以直接將某個數組給另一個數組賦值
var array8 = array6[0...1] //也可將某個數組的某個幾個元素選擇性的給另一個數組賦值
獲取數組元素個數
array6.count //獲取array6的數組元素個數
對數組元素進行遍歷
在循環中可以對數組元素進行修改
for i in 0..<array3.count{
if i == 1{
array3[i] = 10
}
}
只讀循環,不可以修改數組元素
//示例一:
for str in array6{
print(str)
}
//示例二:
for (index, value) in array6.enumerate(){
print("\(index), \(value)")
}
向數組中添加或刪除元素
//添加元素
array6.append("葵")
array6.append("寶")
array6.insert("花", atIndex: 4)
array6.insert("典", atIndex: array6.count)
//刪除元素
//使用array6.remove方法
數組做加法(將多個同類型數組合并起來)
let array9 = array6 + array6
數組的排序
//系統默認升序排列,但可以通過傳參數 > 或 < 實現升降序排列。
let array = [23, 43, 15, 50, 46]
let newArray1 = array.sort() //默認升序排列,可省略 >
let newArray2 = array.sort(<) //傳參數 < 實現降序排列
//以上兩種排序方式都是重新定義的兩個數組,不影響array數組
就地排序
array.sortInPlace()
//以上這種方式是直接給原數組排序,會改變原數組
關于排序的幾種方法(了解)
簡單的選擇排序(按從大到小排列)
思想:將兩個數組元素兩兩比較,如果后一個元素大于前一個元素,就用索引記錄下該元素的下標,再利用元組賦值將其交換
var array = [92,43,87,34,65]
var minIndex = 0
for i in 0..<array.count - 1{
var minIndex = 0
for j in i + 1...array.count - 1{
if array[j] < array[minIndex] {
minIndex = j
}
(array[minIndex], array[j]) = (array[j], array[minIndex])
}
}
print(array)
冒泡排序(從小到大排序)
像氣泡一樣大的往上升
for i in 0..<array - 1{
for j in 0..< array.count - 1 - i {
if array[j] > array[j + 1]{
(array[j], array[j + 1]) = (array[j + 1], array[j])
}
}
}
冒泡排序升級版
思想:當經過某一輪比較后沒有發生兩兩交換,就表示已經排序好了,那就終止循環,不用再排序了
var array = [92,43,87,34,65]
for i in 0..<array.count - 1{
var swapped = false
for j in 0..<array.count - 1 - i{
if array[j] > array[j + 1]{
(array[j], array[j + 1]) = (array[j + 1], array[j])
swapped = true
}
}
if swapped == false {
break
}
}
print(array)
數組的重要應用(也是后面的閉包知識)
let array = [12, 32, 54, 83, 34, 23, 97, 76, 45, 68]
//一:過濾:篩選出需要的數據
//過濾出值大于50的元素(保留50以上的值)
let newArray1 = array.filter{ $0 > 50}
print(newArray1)
//過濾出偶數(取偶數)
let newArray2 = array,filter{ (x: Int) -> Bool in
return x % 2 == 0
}
print(newArray2)
//二:映射:通過某種映射公式計算出映射值
let newArray3 = array.map { (x: Int) -> Int in
return x * x
}
let newArray4 = array.map{$0 * $0}//上面也可以也成這種形式,結果一樣
let newArray5 = array.map{sqrt(Double($0))}
print(newarray3, newArray4, newArray5)
//三:縮減: 通過某種方式得出最終想要的值
//通過將所有值加到一起實現數組的縮減(加減乘除等運算符都是函數)
let newArray6 = array.reduce(0,combine: + )
print(newArray6)
//通過取最大值將數組縮減
let newArray7 = array.reduce(array[0]) { $1 > $0 ? $1 : $0 }
print(newArray7)
集合與字典
集合
集合的定義:
????????????????
字典
字典:存放鍵值對組合的容器
字典是由兩部分組成,其中冒號前面是鍵,后面是值。通過鍵獲取后面與之對應的值,字典是可空類型的,因為所給的鍵可能沒有與之對應的值。數組是有序的,字典是無序的.
字典的基本使用
//創建一個dict字典
var dict: [String: String] = ["a": "阿西吧", "y": "亞麻得"]
//向字典里添加元素
dict["shit"] = "狗屎"
dict["ouba"] = "歐巴"
//向字典里刪除元素a所對應的值
dict.removeValueForKey("a")
//將字典里的某一個key的值賦值為nil,也可以實現刪除元素的功能
dict["y"] = nil //這樣”亞麻得“就被刪除了
//修改元素
dict["ouba"] = "牛糞"
//字典的遍歷
//1.遍歷字典的value
for value in dict.value{
print(value)
}
//2.遍歷字典的key
for key in dict.keys{
print(key)
}
//3.通過元組獲得字典中的鍵和值
for (key, value) in dict{
print("\(key) ---> \(value)")
}
函數與閉包
函數
函數的定義和調用
函數的定義
func 函數名(參數列表)-> 返回值{ 代碼塊 }
參數名有內部參數名與外部參數名之分,如果不寫外部參數名那么內部參數名也是外部參數名,且第一個外部參數名省略。外部參數名可以用下劃線代替,表示內部參數名與外部參數名一樣,且省略外部參數名省略。如果寫明外部參數名,那么外部參數名就不可以省略
函數聲明:
函數名1(外部參數名 內部參數名: 類型, 外部參數名 內部參數名: 類型)-> 返回類型
函數名2(參數名:類型, 參數名: 類型)-> 返回類型
函數的調用:
函數名1(外部參數名: 傳值, 外部參數名: 傳值)
函數名2(傳值, 內部參數名(也就是外部參數名): 傳值)
函數的基本使用
//打印 hello XXX
func sayHello(personName: String) -> String{
let greeting = "hello," + personName + "!!!"
//如果函數的返回類型不是void,那么函數中一定有return語句
return greeting
}
//調用上述函數,參數名personNam被省略
print(sayHello("王大錘"))
func myMax(a x: Int, b y: Int){
return x > y ? x : y
}
//在調用上述函數的時候,由于定義函數的時候沒有寫外部參數名所以所以第一個外部參數名省略,第二個外部參數名與內部參數名默認一致并且要寫
print(myMax(2, y: 5))
func myMin(a x: Int, b y: Int){
return x > y ? y : x
}
//在調用上述函數的時候,由于定義函數的時候寫清楚了外部參數名所以所以外部參數名都要寫
print(myMin(a: 2, b: 5))
//定義函數的時候可以給函數設定默認值
//如果調用函數的時候沒有給函數賦值,那么就直接使用默認值
func sum(a: Int = 1, b: Int = 2, c: Int = 3) -> Int{
return a + b + c
}
//直接使用默認值的情況
print(sum())
//重新給函數賦值,就會按新的值計算
print(sum(4, b: 4, c: 5))
//參數是可變列表(函數參數個數可以是任意多個)
func sum(nums: Int ... ) -> Int{
var total = 0
for num in nums{
total += num
}
return total
}
let a = sum(2,3,4)
let b = sum(3,4,5,6)
print(a , b)
//使用元組來讓函數一次性返回多個值
//定義一個返回最小值與最大值的函數
func minMax(array: [Int]) -> (min: Int, max: Int)? {
//如果數值為空,就返回nil,所以元組為可空類型,需要寫問號 ?
if array.isEnpty{ 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)
}
/函數可以嵌套使用。被嵌套的函數可以訪問外側函數的變量,可以使用嵌套函數來重構一個太長或者太復雜的函數
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
//函數調用的傳參只是傳值
//正常情況下,函數不能修改外部參數
//inout - 輸入輸出參數(不僅將數據傳入函數還要從函數中取出數據)
//inout既可以傳參進去,也要傳出來(用于修改外部參數)
func createX(inout x: Int){
x = 100
}
var x = 1
//將外部參數x修改后傳出來,注意要寫取地址 & 符號
createX(&x)
print(x)
//???????????????????
//函數是第一等類型,這意味著函數可以作為另一個函數的返回值
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
//函數也可以當做參數傳入另一個函數
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
//hasAnyMatches(numbers, lessThanTen)
hasAnyMatches(numbers, condition: lessThanTen)
示例程序
//設計一個函數傳人兩個正整數m和n,計算從m加到n的和
var total = 0
func sum(m: Int, _ n: Int) -> Int{
let (a, b) = m > n ? (n, m) : (m, n)
for i in a...b{
total += i
}
return total
}
print(sum(3, 5))
//設計一個函數輸入三條邊的長度,判斷能不能構成三角形
func isTriangle(a: Int, b: Int, c: Int) -> Bool{
return a + b > c && a + c > b && b + c > a
}
//設計一個函數,傳人年月日,返回該日期是這一年的第幾天
func dayOfYear(year: Int, month: Int, day: Int) -> Int{
var totalDays = 0
for i in 1..<month{
let days = monthDays(year, month: i)
totalDays += days
}
totalDays += day
return totalDays
}
func monthDays(year: Int, month: Int) -> Int{
var days = 31
if month == 4 || month == 6 || month == 9 || month == 11{
days = 30
}else if month == 2{
if isLeapYear(year){
days = 29
}else{
days = 28
}
}
return days
}
func isLeapYear(year: Int) -> Bool{
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0
}
print(dayOfYear(2016, month: 12, day: 31))
//設計一個函數計算組合數C(m, n) = m! / n! / (m - n)!
func combine(m: Int, n: Int) -> Int{
//斷言,如果不滿足條件,程序運行時就會死在這里并提醒函數的使用者
assert(m > n, "m必須大于n")
return factorial(m) / factorial(n) / factorial((m - n))
}
//求階乘
func factorial( n: Int) -> Int{
var facN = 1
for i in 1..<n{
facN *= i
}
return (facN)
}
函數也是一種類型,也可以用常量或變量來定義??梢詫⒑瘮诞斪髁硪粋€函數的參數活著返回值來使用
func sum (a: Int, _b: Int) -> Int{
return a + b
}
func mul(a: Int, b: Int) -> Int{
return a * b
}
//用一個變量來保存函數,可以實現一個函數擁有多項功能,可以將不同的運算函數賦給fn,那么將來如果要使用其他運算也可以由fn代替了
var fn = sum
//將函數fn當參數傳入foo函數
func foo(array: [Int], fn: (Int, Int) -> Int) -> {
var sum = array[0]
for x in array[0..<array.count]{
sum = fn(sum, x)
}
return sum
}
//調用函數的時候傳入函數參數的時候直接寫函數名就可以了
let a = [1, 3, 5, 7, 8]
print(foo(a, fn: fn))
知識小結:
當調用上述foo函數時,第二個參數可以傳什么?
1.傳入所有的自定義的==(Int, Int) -> Int==類型的函數
==print(foo(a, fn: mul))==
2.傳入二元運算符:+-*/ 因為二元運算符就是函數
==print(foo(a, fn: +))==
3.傳入==匿名函數(閉包)==
如果函數的最后一個參數是閉包可以寫成尾隨閉包的形式, 也就是將閉包放到函數參數的圓括號外面寫在一對花括號中, 如果函數后面有尾隨閉包且函數的圓括號中沒有參數, 那么函數的圓括號也可以省略(僅限于有尾隨閉包的場景)
//還是接上一個示例程序
//3.1閉包的完整寫法
foo(a, fn: {(a, b) -> Int in
return a + b
})
//3.2省略掉類型和不必要的括號
foo(a, fn:{ a, b in a + b})
//3.3省略參數名
foo(a, fn: {$0 + $1})
//3.4尾隨閉包
foo(a){(a, b) -> Int in
return a + b
}
閉包
閉包是一種匿名函數,跟 C ,OC 中的 block 以及 C# , C++, Java中的 lambda 表達式相似,代碼的終極原則是高內聚,低耦合,而函數的閉包就是為了降低代碼的耦合性而生的
//給數組排序
var array = ["game", "cat", "abacus", "good", "dislike", "dog"]
//1.傳入二元運算符
array.sortInPlace(>)
//如果函數參數的最后一個參數是閉包,可以寫成尾隨閉包的形式,也就是將閉包放到函數的圓括號外面,寫在花括號中。如果函數后面有尾隨閉包且函數的括號中沒有參數,那么函數的圓括號也可以省略
//2.尾隨閉包寫法1
array.sortInPlace(){$0 > $1}
//3.尾隨閉包寫法2
array.sortInPlace{$0 > $1}
//按字母個數排序
array.sortInPlace{(one, two) -> Bool in
//characters:代表字符串里的所有字符
return one.characters.count < two.characters.count
}
函數的遞歸調用
函數自己調用自己。需要注意的時必須判斷函數什么時候停下來,否則函數就會一直調用,直到內存耗盡。遞歸調用通常只用于循環不能解決的問題,比如迷宮,漢若塔等。遞歸效率太低,一般不用
使用函數遞歸必須合理使用以下兩點
1.遞歸公式
2.收斂條件
//計算1到 n 的和
func sum(n: Int) -> Int {
if n == 1 {
return 1
}
return n + sum(n - 1)
}
//漢若塔問題
func hanoi(n: Int, _ a: String, _ b: String, _ c: String){
if n > 0 {
//第一步:將n - 1個盤子移動到c柱子
//第二步:將第n個盤子移動到b柱子
//第三步:將n - 1個柱子移動到b柱子上
hanoi(n - 1, a, c, b)
print("\(a) --> \(b)")
hanoi(n - 1, c, b, a)
}
}
面向對象編程
面向對象編程步驟:
一:定義類(如果需要的類蘋果官方已經提供,就可以直接進入第二步)
二:創建對象(創建對象必須調用初始化方法)
三:給對象發消息
面向對象編程的基本常識
- 定義類就可以創建出新類型
- 變量定義在類的外面就叫做變量 - variable
- 變量定義在類中就叫做屬性 - property
- 函數寫在類的外面就叫做函數 - function
- 函數寫在類的里面就叫做方法- method
- 數據抽象(找名詞,找到與該類相關的屬性)與行為抽象(找動詞,找到與該類相關的方法)
- 類的屬性都需要經過初始化方法init初始化后才能使用,初始化方法應該保證所有的存儲屬性都被初始化(有值)。但如果數據抽象出來就給他賦值,就不需要初始化(但不推薦這樣寫,因為這樣會多消耗內存。有的屬性還沒用到就已經給它賦值了,就會占用不必要的內存)
- 計算屬性 - computational property (通過對存儲屬性做運算得到的屬性),通常獲得某個計算出來的值的方法都可以設計成計算屬性。計算屬性可以用get和set方法獲取或修改屬性值
類與對象
類是對象的藍圖和模板,為對象提供了屬性和方法。
那什么是對象呢?
萬物皆為對象,只要是我們看得見,摸得著,或者感受得了的都可以當做對象。比如說空氣就是一個對象,它屬于氣體類,擁有透明的顏色、壓強等屬性,空氣能流動、能提供給動物呼吸,這就是它的行為(也稱為方法)
對象都屬于類,并且每個對象都是獨一無二的。類是抽象的,對象是具體的
類的設計:
1.找出該類的相關屬性(包括存儲屬性和計算屬性),其中計算屬性是通過對存儲屬性做運算得到的,計算屬性通常屬于只讀屬性,所以只有get,沒有set
2.初始化存儲屬性:我們可以在一個類中定義多個初始化方法,其中初始化方法包括兩種。
2.1便利初始化方法/便利構造器:調用了其他初始化方法飛初始化方法,需要在init前面加上 convenience 關鍵字
2.2指派初始化方法/指構造器: 被其他初始化方法調用的初始化方法,(主要用來初始化所有存儲屬性)
2.3 指派構造器前面加上required可以將構造器指定為必要構造器, 所謂的必要構造器意味著子類也要提供一模一樣的構造器
3.找出與該類相關的方法
4.如果在特定的應用場景中發現現有的類缺少了某種功能,那么可以通過類擴展(extension)的方式現場添加這項功能
示例1:
知識點:
==便利構造器與指派構造器的使用==
1.一個類中可以存在多個初始化方法
2.利用關鍵字convenience可以定義一個便利初始化方法,這個方法調用的是其它初始化方法
3.創建對象的時候就可以選擇調用需要的初始化方法
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
}
//計算與其他點的距離
func distanceTo(other: Point) -> 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
}
//移動多少距離的方法
func moveBy(dx: Double, _ dy: Double) {
x += dx
y += dy
}
}
必要構造器
指派構造器前面加上required可以將構造器指定為必要構造器,所謂的必要構造器就意味著子類也要提供一模一樣的構造器
class Person {
var name: String
var age: Int
required init(name: String, age: Int) {
self.name = name
self.age = age
}
// 便利構造器(convenience)
convenience init() {
self.init(name: "無名氏", age: 20)
}
}
class Student: Person {
var major: String
required init(name: String, age: Int) {
major = "未知"
super.init(name: name, age: age)
}
convenience init(name: String, age: Int, major: String) {
// 下面的語句必須寫在調用自己的初始化方法之后否則major屬性會被賦上不正確的值
// self.major = major
self.init(name: name, age: age)
self.major = major
// 初始化的第一階段
// 1. 初始化自己特有的屬性
// self.major = major
// // 子類只能調用直接父類的構造器
// // 子類構造器必須調用父類的非便利構造器(指派構造器)
// // super.init() // compiler error
// // 2. 調用父類的初始化方法
// super.init(name: name, age: age)
// // 初始化的第二階段
// // 此處可以調用對象的方法因為對象已經完成了初始化
// study()
}
func study() {
print("\(name)正在學習.")
}
}
示例2
知識點:
==類擴展(extension)==
給已有的類添加新的方法
//通過類擴展(extension)的方式給UIColor添加一項隨機生成一種顏色的功能
//為了得到一個min~max范圍內的隨機整數
func randomInt(min: UInt32, _ max: UInt32) -> Int {
return Int(arc4random_uniform(max - min + 1) + min)
}
//通過extension給UIColor擴展一個randomColor()方法
extension UIColor {
static func randomColor() -> UIColor {
let r = CGFloat(randomInt(0, 255)) / 255.0
let g = CGFloat(randomInt(0, 255)) / 255.0
let b = CGFloat(randomInt(0, 255)) / 255.0
return UIColor(red: r, green: g, blue: b, alpha: 1)
}
}
示例3
知識點:
1==類的修飾符==
- public (公共的) 可以在其它項目中使用(只需要將文件拷貝到其它項目中即可)
- internal(內部的)系統默認的修飾符,只能在項目內實現訪問
- private(私有的)只能在該類里面進行訪問,一般用來保護數據
修飾符的使用一般遵循以下兩點:
1.存儲屬性通常是private的,因為數據需要保護起來,不能讓外界隨意修改,并且有些屬性還必須隱藏一部分信息,比如銀行卡卡號和用戶姓名等信息需要隱藏一部分
2.方法一般是public的,因為方法是對象接收的消息,如果自定義的類沒有打算在其他項目里面使用,可以不寫訪問修飾符,直接使用默認的internal修飾符,表示在只本項目中公開,對其他項目私有。
2==文檔注釋==
一個好的程序員都應該有寫注釋的習慣,注釋不僅能讓別人看懂自己寫的代碼,還可以方便以后自己看懂(自己寫的代碼過一段時間自己都不一定能看懂)。一個有逼格的程序員,會用文檔注釋來使代碼清晰直觀。文檔注釋能夠在其它調用它的地方通過alt來查看方法說明、參數說明和返回值。能讓人一眼就能看懂這個類是干什么的
//創建一個學生類,要求對學生姓名設置隱私保護
// 學生
public class Student {
private var _name: String
private var _age: Int
/// 學生姓名隱去最后一個字符
//用get方法獲取對象屬性,并隱藏部分信息
public var name: String {
get {
let displayName = _name.substringToIndex(_name.endIndex.advancedBy(-1))
return displayName + "*"
}
}
// 學生的年齡
public var age: Int {
get { return _age }
}
/**
初始化方法
- parameter name: 姓名
- parameter age: 年齡
*/
public init(name: String, age: Int) {
_name = name
_age = age
}
/**
吃飯
- parameter food: 吃的東西
*/
public func eat(food: String) {
print("\(_name)正在吃飯.")
}
/**
學習
- parameter courseName: 課程的名稱
- parameter hour: 學習時間
- returns: 學會了返回true否則返回false
*/
public func study(courseName: String, hour: Int) -> Bool {
print("\(_name)正在學習\(courseName).")
return hour > 180 ? true : false
}
/**
看片
*/
public func watchJapaneseAV() {
if _age >= 18 {
print("\(_name)正在觀看島國愛情動作片.")
}
}
}
示例4
知識點:
1.==短除法(歐幾里得算法)==
如果 Y > X , X 和 Y 的最大公約數跟 Y % X 和 X 的最大公約數相等
2.==運算符重載==
swift中所有運算符都是函數,內置運算符能運算的類型太局限,如果我們自定義的某個類型也需要用到運算符,這時候就需要對運算符進行重載,來實現自己定義類型的運算。運算符重載后可以直接用運算符進行運算,避免了調用方法,能夠使代碼更直觀
3.==級聯式編程==
級聯式編程是定義的方法的返回值是對象本身,如果多個方法都是返回對象本身,就可以同時調用,形成一個鏈狀的代碼,也稱之為開火車式的編程
//短除法算最大公倍數,可以提高性能
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)
}
//將分數正規化,如果分母為0,分子分母同時乘以 -1
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)
}
func *(one: Fraction, two: Fraction) -> Fraction {
return one.mul(two)
}
func /(one: Fraction, two: Fraction) -> Fraction {
return one.div(two)
}
繼承和多態
繼承
繼承是從已有的類創建新類的過程。提供繼承信息的類稱為父類(超類/基類),得到繼承信息的類稱為子類(派生類/衍生類)。
通常子類除了得到父類的繼承信息還會增加一些自己特有的東西,所以子類的能力一定比父類的功能強大。繼承的意義在于子類可以復用父類的代碼并且增強系統現有的功能
示例程序:
定義一個父類 Person :
enum Sex {
case Male
case Female
}
class Person {
var name: String
var age: Int
var sex: Sex
//初始化Person的屬性
init(name: String, age: Int, sex: Sex){
self.name = name
self.age = age
self.sex = sex
}
//吃的方法
func eat () {
print("吃")
}
}
定義一個繼承 Person 的子類 Student :
//在 Student 后面加上一個冒號,然后寫上另一個類,就表示前面一個類繼承后面的類
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)
}
//Student的學習行為
func study(corseName: String) {
print("\(name)是\(major)專業的學生")
print("\(sex == .Male ? "他":"她") 正在學習\(corseName)")
}
}
定義一個繼承 Person 的子類 Teacher :
class Teacher: Person {
//teacher的職稱屬性
var title: String
//初始化
init(name: String, age: Int,title: String, sex: Sex){
self.title = title
super.init(name: name, age: age, sex: sex)
}
//teacher的教書行為
func teach(corseName: String) {
print("\(name)是\(title)")
print("\(sex == .Male ? "他":"她") 正在教\(corseName)")
}
}
main函數:
//創建對象
let p1 = Person(name: "大傻子", age: 20, sex: .Male)
p1.eat() //p1對象只有eat行為
let stu = Student(name: "二傻子", age: 15, major: "計算機計算", sex: .Male)
stu.eat()
stu.study("計算機科學")//stu對象除了有eat行為,還增加了study行為
let t = Teacher(name: "老傻子", age: 50, title: "叫獸", sex: .Male)
t.eat()
t.teach("c語言") //t對象除了有eat行為,還增加了teach行為
//以上可以看出,父類對象的功能比子類對象的少,子類除了得到父類的繼承信息還會增加一些自己特有的東西
//注意:
//我們可以將子類型的對象賦值給父類型的變量(因為子類型跟父類型直間的關系是 is - a 關系)
//學生是人,老師是人,所以學生對象和老師對象可以直接賦值給人類型的變量
let person1: Person = Student(name: "張三", age: 20, major: "計算機網絡", sex: .Male)
let person2: Person = Teacher(name: "李四", age: 50, title: "叫獸", sex: .Male)
//如果要將父類型的變量轉換為子類型,需要用as運算符進行類型轉換
//如果能確定父類型的變量就算某種子類型對象,可以用as!進行轉換
//如果不能確定父類型的變量是哪種子類型可以用as?嘗試轉換
(person1 as! Student).study("計算機網絡")//確定person1是Student類型,用as!進行轉換
(person1 as! Teacher).teach("計算機網絡")//程序會在這里崩潰,因為感嘆號 ! 代表person1肯定是Teacher類型,系統會將它當做Teacher使用,然而person1并沒有teach方法,所以程序會在這里崩潰
//為了解決當不知道對象屬于哪種類型的問題,使用as?進行判斷
if let temp = person2 as? teacher{
print("你是老師,來教書吧")
temp.teach("C語言")
}else{
print("你不是老師,滾吧")
}
多態
什么是多態?
同樣的對象類型,接收相同的消息(調用相同的方法),但可以實現做不同的事情,這就是多態。
實現多態的關鍵步驟:
1.方法重寫:子類在繼承父類的過程中對父類已有的方法進行重寫,而且不同的子類給出不同的實現版本
2.對象造型:將子類對象當做父類型來使用,然后再通過 if + as?將父類型安全的轉換為子類型,然后再調用子類特有的方法
示例程序:
定義一個父類 寵物 - Pet
//定義一個父類對象(動物Pet)
class Pet {
var nickname: String
var gender: Gender
var age: Int
init(nickname: String, gender: Gender, age: Int) {
self.nickname = nickname
self.gender = gender
self.age = age
}
func eat() {
print("\(nickname)正在吃東西.")
}
func play() {
print("\(nickname)正在玩耍.")
}
func shout() {
print("\(nickname)發出了叫聲.")
}
}
定義一個子類 貓-Cat
// Cat和Pet之間是IS-A關系(繼承)
class Cat: Pet {
var hairColor: String?
// 父類有的方法子類可以重新實現 這個過程叫方法重寫
// 需要在方法前添加override關鍵字
// 重寫有時也被稱為置換/覆蓋/覆寫
override func play() {
super.play()
print("\(nickname)正在玩毛線球.")
}
override func shout() {
print("\(nickname): 喵喵喵……")
}
func catchTheMouse() {
print("\(nickname)正在抓老鼠.")
}
}
定義一個子類 狗-Dog
class Dog: Pet {
var isLarge: Bool//是否為大狗
init(nickname: String, gender: Gender, age: Int, isLarge: Bool) {
self.isLarge = isLarge
super.init(nickname: nickname, gender: gender, age: age)
}
override func play() {
super.play()
print("\(nickname)正在接飛碟.")
}
override func shout() {
print("\(nickname): 旺旺旺……")
}
func keepTheDoor() {
if isLarge {
print("\(nickname)正在看門.")
}
else {
print("\(nickname)太小了誰也打不過.")
}
}
}
定義一個子類對象 情婦-Mistress
class Mistress: Pet {
override init(nickname: String, gender: Gender, age: Int) {
super.init(nickname: nickname, gender: gender, age: age)
}
override func play() {
print("\(nickname)正在購物.")
}
override func shout() {
print("此處省略一萬字,少兒不宜!")
}
func makeTrouble() {
print("\(nickname): 我懷孕了……")
}
}
定義一個Person對象,Person與Pet之間是 has - a 關系(人擁有寵物)
class Person {
var name: String
var pet: Pet? //人可以擁有寵物,也可以不擁有,所以加個問號
init(name: Sting){
self.name = name
}
func adopt(pet: Pet) {
print("\(name)領養了\(pet.name)")
self.pet = pet //人的pet屬性賦值為傳入的pet(表示人現在的pet就是函數傳進來的pet)
}
//人打寵物的行為
func strikePet(){
//判斷人是否有寵物
if let pet = pet{
print("\(name)正在打\(pet.name)")
pet.shout() //人的寵物叫
}else{
print("\(name)正在自虐")
}
}
func play(){
//判斷人是否有寵物
if let pet = pet{
print("\(name)正在和\(pet.name)玩耍")
}else{
print("\(name)正在自嗨")
}
}
}
main函數
let person = Person(name: "王大錘")
let petArray = [
Dog(nickname: "小黃", gender: .Male, age: 1, isLarge: true),
Cat(nickname: "小咪", gender: .Female, age: 1),
Mistress(nickname: "小美人", gender: .Female, age: 24)
]
//以下便是多態的應用
for pet in petsArray{
//三種寵物都是同樣對象類型 - Pet
//三種寵物都有相同的行為,但打印結果是按照不同寵物量身定制的
//不同的動物執行相同的方法,結果不同
pet.eat()
pet.play()
pet.shout()
//判斷是什么動物,不同的寵物執行不同的方法,通過 if - as? 將父類型安全的轉換為子類型,然后再調用子類特有的方法
if let dog = pet as? Dog{
dog.keepTheDoor()
}else if let cat = pet as? Cat{
cat.catchTheMouse()
}else if let mis = pet as? Mistress{
mis.makeTrouble()
}
}
面向協議編程
protocol 協議名稱:父類協議1, 父類協議2,...,{
方法的集合(計算屬性相當于方法)
}
協議是方法的集合(計算屬性相當于就時方法),可以把看似不相關的對象的公共行為放到一個協議中
協議在swift中大致有三種作用:
1.表能力:遵循協議就意味著具備了某種能力
2.表約定:遵循協議就一定要實現協議中的方法
3.表角色:一個類或者結構可以遵循多個協議,一個協議可以被多個類或結構遵循。遵循協議就意味著扮演了某種角色,遵循多個協議就意味著扮演多種角色
協議中可以聲明方法和計算屬性
接口(協議)隔離原則:協議的設計要小而專,不要大而全
協議的設計也要高度內聚
swift中繼承是單一繼承,即一個子類只能有一個父類。遵循協議可以遵循多個
協議擴展extension:可以在協議擴展中給協議的方法提供默認實現,也就是說如果某個類遵循了協議但沒有實現這個方法就直接使用默認實現
協議中全是抽象概念(只有聲明沒有實現)遵協議的類可以各自對協議的方給出不同的實現版本,這樣面向協議編程就可以把多態的優勢發揮的淋漓盡致 可以寫出更通用靈活的代碼(符合開閉原則)
實現開閉原則的關鍵有兩點:
1.抽象是關鍵
2.封裝可變性(橋梁模式 - 將不同的可變因素封裝到不同的繼承結構中)
設計模式
代理模式
-
委托回調
一個對象想做某件事情,但是自身沒有能力做這件事情就可以使用委托回調,具體步驟如下:- 設計協議,被委托方遵循協議,實現方法
- 委托方有一個屬性是協議類型的,通過該屬性可以調用協議中的方法
- 委托方的協議類型屬性通常要寫成weak引用,并且是可空類型
其他
- 協議組合:protocol<協議1, 協議2,... ,>
- 協議方法
- 協議擴展
堆和棧
計算機的硬件由五大部件構成:運算器控制器、存儲器、輸入設備和輸出設備
運算器 + 控制器==>CPU(中央處理器)
存儲器 ==>內存(RAM)
程序猿可以使用的內存大致分為五塊區域:
堆(heap):創建的對象都是放在堆上的。堆的特點是容量大速度慢
棧(stack):定義的局部變量/臨時變量都放在棧上。棧的特點是容量小速度快
靜態區:
-靜態段 - 全局變量
-只讀數據段 - 常量
-代碼段 - 函數和方法
結構的對象是值類型,類的對象是引用類型。值類型在賦值的時候會在內存中進行對象拷貝,引用類型不會進行對象拷貝只是增加了一個引用
我們在定義新類型時優先考慮使用類而不是結構,除非要定義一些底層的數據結構(保存其他數據類型)
泛型
泛型,讓類型不再是程序中的硬代碼
- 泛型函數
- 泛型類、結構、枚舉
相關知識
- 泛型限定
- where子句
swift中的類、結構和枚舉都可以使用泛型
//給數組排序的函數
<T: Comparable>限定T類型必須是遵循了Comparable協議的類型
func bubbleSort<T: Comparable>(array: [T]) -> [T] {
var newArray = array
for i in 0..<newArray.count - 1 {
var swapped = false
for j in 0..<newArray.count - 1 - i {
if newArray[j] > newArray[j + 1] {
mySwap(&newArray[j], &newArray[j + 1])
swapped = true
}
}
if !swapped {
break
}
}
return newArray
}
在該段代碼中,數組類型是泛型,所以數組可以是任意類型
定義一個虛擬類型T調用函數時根據函數傳入的參數類型來決定T到底是什么
錯誤處理
- throw
- throws/throws
- do
- catch
- try
其他
- ARC
- 正則表達式
- 嵌套類型
swift中可選類型與if - let 語法
var count: Int?
count = 100
if let validCount = count {//如果count有值的話,把count的值賦給validCount,且條件為true;如果count為nil,則執行else
"count is " + String(validCount) //count的值為100
} else {
"nil"
}