Swift 学习 准备工作【TSPLv1.8+个人理解】

声明:非转非完全原创但是没有别的适合分类
来源学习 The Swift Programming Language v 1.8 中文版 以后简称【TSPLv1.8】正常来说应该分为学习笔记类的吧,但是没有这样的分类,部分使用xcode 进行校验,但是因本人知识有限,所以部分说明文档不能正确理解,后续文章可能会说明。

• 简单值(Simple Values) 
     
要创建一个空数组或者字典,使用初始化语法

let emptyArray = [String]()
let emptyDictionary = [String: Float]() //这种形式其实就是 let dictionary = ["Key" : "Value"] 的形式

如果类型信息可以被推断出来,你可以用[] 和[:] 来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样
shoppingList = []
occupations = [:] 《 这里只是作为一种说明 
// 以下是字典声明的一种方式 
// var emptyDict :[String :Int] = [:]
//print(emptyDict)  
//@end


print("\(1.0+2.0-3.0*4.0/5.0)") //swift是可以这样写来计算打印的

var keyValue=["Oracle":"Java","Apple":"Objective",]
keyValue["China"] = "Made in China"
print(keyValue)
//以上的写法得到的结果是 ["China": "Made in China", "Oracle": "Java", "Apple": "Objective"]
//也就是说 新键入的内容会插在数组的前面  [栈] 的存储方式

• 控制流(Control Flow) 


使用 ifswitch 来进行 条件操作,使用 for-in 、 forwhilerepeat-while 来进行 循环
包裹条件和循环变量括号可以省略, 但是语句体的大括号是必须的

在if 语句中,条件必须是一个布尔表达式

你可以一起使用if 和let 来处理值缺失的情况
这些值可由可选值来代表
一个可选的值是一个具体的值或者是nil 以表示值缺失
在类型后面加一个问号来标记这个变量的值是可选的


字符串可以赋值为可选值 比如
var str :String? = "str1" //打印结果是  Optional("str1")
但 nil是字符串的默认值 如果 
var str :String? = nil //打印结果是 nil

举个例子 
var testName:String? = "World"
var geeting = "Hi"
if let name = testName {
    geeting = "Hello, \(testName)"
}
print ("\(geeting)")

打印结果为  Hello, Optional("World")

var testName:String? = nil
var geeting = "Hi"
if let name = testName {
    geeting = "Hello, \(testName)"
}
print ("\(geeting)") 
打印结果为 Hi

如果变量的可选值是nil ,条件会判断为false ,大括号中的代码会被跳过
如果不是nil ,会将值解包并赋给let 后面的常量,这样代码块中就可以使用这个值了


处理可选值的方法是通过使用?? 操作符来提供一个默认值。如果可选值缺失的话,可以使用默认值来代替
举个例子
let nickname :String? = nil
let fullname :String = "Yohann Whole"
let informalGeeting  = "Hi \(nickname ?? fullname)"
print ("\(informalGeeting)")
得到的结果是 Hi Yohann Whole
let nickname :String? = "nil"
let fullname :String = "Yohann Whole"
let informalGeeting  = "Hi \(nickname ?? fullname)"
print ("\(informalGeeting)")
得到的结果是 Hi nil

结论:??的作用 有点类似 Java 里三目运算符 的 if()?a:b 选择满足条件的一个,不过
?? 如果两个值不缺失(且都不是可选的或者nil) 后面的一个会以可选的方式给出
如果两个都是可选的nil 那么得到的结果 就会直接以默认值给出 
如果存在非可选 与可选 及 ?? 前后两个中存在一个非可选, 一个可选那么直接将非可选的值列出,而可选的值不给出。

switch 支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?")
default:
    print("Everything tastes good in soup.")
}
练习: 删除default 语句,看看会有什么错误?
注意let 在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量x 
添加另一个变量来记录最大数字的种类(kind),同时仍然记录这个最大数字的值。
let interestingNumbers = [
    "Prime" : [2,3,5,7,11,13,17,19,23,29],
    "Fibonacci" : [1,1,2,3,5,8],
    "Square" : [1,4,9,16,25],
]
var largest = 0
var kindest = ""
for (kind,numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
            kindest = kind
        }

    }
}
print (kindest,largest)

var total = 0
for i in 0..<5{ //..< number 表示number 为循环的上限 且不包含上限   如果需要包含上限 使用 ...
    total += i
}
print (total) // 运行结果10
使用..< 创建的范围不包含上界,如果想包含的话需要使用... 

• 函数和闭包(Functions and Closures) 

func geet(str1 :String ,str2 :String ) -> String { // func 是函数的声明, -> 后面跟函数的返回值
    return "\(str1) , \(str2)"
}

print (geet(str1:"Hello",str2:"World"))

func add (num1 :Int, num2 :Int) -> Int {
    return num1 + num2 // 注意 这里因为上面返回值为Int 所以这里不能写成 "\(num1 + num2)" [如果写成这样,则会出现cannot convert return expression of type 'String' to return type 'Int']的错误
}
print(add(num1:3,num2 :5))

var allZeros = [Int](count :5 ,repeatedValue:0)
print (allZeros)  //运行结果  [0, 0, 0, 0, 0]

默认情况下,函数使用它们的参数名称作为它们参数的标签,在参数名称前可以自定义参数标签,或者使用_  表示不使用参数标签。
func geet (_ person :String ,_ day :String ) -> String  { //如果此处都用 _表示 不是用参数  下面会以默认的顺序进行输出打印
    return "Hello \(person) ,today is \(day)"
}
print (geet("John" ,"Wednesday"))  // Hello John ,today is Wednesday

使用元组来让一个函数返回多个值。该元组的元素可以用名称或数字来表示
func calculateStatustics(scores:[Int]) -> (min:Int ,max:Int ,sum :Int){
    var min = scores[0]
    var max = scores[0]
    var sum = 0
    for score in scores {
        if score > max {
            max = score
        }
        else if score < min  {
            min = score
        }
        sum += score
    }
    return (min ,max,sum)
}
let statistics = calculateStatustics(scores:[5,3,100,3,9]) //这里的 : 容易漏掉
print(statistics.0)
print (statistics.sum)

func mulOf(numbers: Int...) -> Int { // ... 表示参数可变(0 ~ n)
    var mul = 1
    for number in numbers {
        mul *= number
    }
    return mul
}
print(mulOf()) // 1
print(mulOf(numbers: 643,64,3)) //123456

函数可以嵌套。
被嵌套的函数可以访问外侧函数的变量,可以使用嵌套函数来 重构一个太长或者太复杂的函 数。
func returnFifteen() ->Int {
    var x = 10  //【个人理解】 x在这里定义为在returneFifteen 函数下的“全局变量”
    func add() {
        x += 5
    }
    add() //这里x + 5
    return x  
}
print(returnFifteen()) //得到 return x的值

函数是第一等类型,这意味着函数可以作为另一个函数的返回值
func makeIncrementer() -> ((Int) -> Int) { //这里的 ((Int) -> Int) 其实就是把 addOne 也就是addOne 当作 makeIncrementer()的返回值
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne  // 相当于 return  return 1+ number
}
var increment = makeIncrementer()
increment(7)  
函数也可以当做参数传入另一个函数
func hasAnyMatch(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]
var result = hasAnyMatch(list:numbers,condition:lessThanTen)
print(result)


func hasAnyMatch(list:[Int],condition:(Int)-> Bool)->Bool{
    for item in list {
        if condition (item){
            return false
        }
    }
    return true
}
func lessThanTen(number :Int)->Bool{
    return number < 10
}
var numbers = [20,19,7,12]
var result = hasAnyMatch(list:numbers,condition:lessThanTen)
print(result)
//【一点启蒙】同过改变条件结果来得到期望的结果  即:1为只要存在一个满足  lessThanTen 就会返回ture ;2为都不满足才返回true
虽然 2在现实当中存在不符合逻辑的点 但稍微改变 将 return number > 10 之后 2可以得到numbers 都为小于 10 的时候返回true 
举一反三的立正】
@end

函数实际上是一种特殊的闭包:它是一段能之后被调取的代码
闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到
你可以使用{} 来创建一个匿名闭包
使用in 将参数和返回值类型声明与闭包函数体进行分离。

用文字来描述Swift定义基本函数的语法就是: func 函数名 (形参列表) -> 返回值类型 { 函数体},这样你就可以定义一个函数了。
可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用
     当一个闭包作为最后 一个参数传给一个函数的时候,它可以直接跟在括号后面
     当一个闭包是传给函数的唯一参数,你可以完全忽略 括号 【  Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 lambdas 函数比较相似。

let sortedNumbers = numbers.sort { $0 > $1 }
print(sortedNumbers)

• 对象和类(Objects and Classes) 

使用class 和类名来创建一个类
类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类
同样,方法和函数声明也一样

要创建一个类的实例,在类名后面加上括号
使用点语法来访问实例的属性和方法。

class Shape {
    var numberOfSides = 0
    let numberLet = 3.14
    func simpleDesc() -> String {
        return "A Shape with \(numberOfSides) sides"
    }
    func takeALet(_ A : Double) -> String { // 这里需要使用 Double 如果 是Float 会提示 “损失精度”的错误
        let mul = A * numberLet
        return "A is let and is \(mul)" // 如何在return  里进行计算呢?
    }
}
var shape = Shape()
shape.numberOfSides = 12
let show1 = shape.simpleDesc()
let show2 = shape.takeALet(4.12)
print (show1)   //  A Shape with 12 sides
print (show2)  //  A is let and is 12.9368
这个版本的Shape 类缺少了一些重要的东西:一个构造函数来初始化类实例
完整程序 :
class Shape {
    var numberOfSides = 0
}

class NamedShape {
    var numberOfSides: Int = 0
    var name: String
    init(name: String) {
        self.name = name  // self 被用来区分实例变量
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

class Square :NamedShape {
    var sideLength : Double
    init(sideLength :Double ,name :String ) {  
        self.sideLength = sideLength
        super.init (name:name)
        numberOfSides = 4
    }

func area() -> Double {
    return sideLength * sideLength
}
override func simpleDescription () -> String {
    return "A Square with \(numberOfSides) sides of length \(sideLength)."
    }
}
let test = Square(sideLength :5.2 ,name:"my test Square") //这里赋值给name 结果没有得到,因为用户 init 当中的初始化。 // 创建实例 test
print(test.area())   //27.04
print(test.simpleDescription()) / /A Square with 4 sides of length 5.2.
print(test.name) // my test Square

创建实例的时候,像传入函数参数一样给类传入构造器的参数
每个属性都需要赋值——无论是通过声明(就像numberOfSides )还是通过构造器(就像name )
如果你需要在删除对象之前进行一些清理工作,使用deinit 创建一个析构函数
子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割
除了储存简单的属性之外,属性可以有 getter 和 setter

class Shape {
    var numberOfSide = 0

}
class NamedShape {
    var numberOfSide : Int = 0
    var name :String
    init(name: String) {
        self.name = name
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSide) sides"
    }
}
class EquilateralTriangle :NamedShape {
    var sideLength :Double = 0.0 // 设置子类声明的属性值
//调用父类的构造器
    init(sideLength :Double,name :String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSide = 3
    }
    var perimeter :Double {
//改变父类定义的属性值      如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSetdidSet
        get{ 
            return 3.0 * sideLength
        }
        set{     //set 与Java 的setter 方法类似 (有区别)如果这里不写set 只写 get 方法 那默认是readOnly 属性 
            sideLength = newValue / 3.0 // newValue 可以在set中显示地设一个值 //这里的newValue 与下面的val 是一个意思
        }
    }
    override func simpleDescription() -> String {
        return "An equilateral truagle with \(numberOfSide) side of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
帮助理解的 :

class Calcuator{
    var a:Int = 1
    var b:Int = 1
    var sum :Int {
        get{
            return a + b
        }
        set( val){  // 这里的val的用法 与上面的newValue是一个意思
            b = val - a
        }
    }
}
let cal = Calcuator()
print (cal.sum) // 2
cal.sum = 5
print(cal.b) //4
如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSet 和didSet 。
用法如下
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
     square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
     triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)

处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?
如果? 之前的值是nil , ? 后面的东西都会被忽略,并且整个表达式返回nil
否则, ? 之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength  // "Optional(2.5)\n"

• 枚举和结构体(Enumerations and Structures) 
使用enum 来创建一个枚举

enum可以通过raw value对其进行预填充
enum Rank: Int {
case Ace = 1 //rawValue 使case的初始值为 1   这里如果不进行赋值 则ace的默认值为0
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten //2,3,4,5,6,7,8,9,10 【rawValue 赋值】
case Jack, Queen, King//11,12,13【rawValue赋值】
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.rawValue //得到ace的 初始值 rawValue

默认情况下,Swift 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变
在上面的例子中, Ace 被显式赋值为 1,并且剩下的原始值会按照顺序赋值

你也可以使用字符串或者浮点数作为枚举的原始值。使用rawValue 属性来访问一个枚举成员的原始值。

使用init?(rawValue:) 初始化构造器在原始值和枚举值之间进行转换
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你就不需要 提供原始值

enum Suit{
case Spades,Hearts,Diamonds,Clubs
 func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
func color() -> String {
switch self {
case .Spades , .Clubs:
return "black"
default:
return "red"
}
}
}
let hearts = Suit.Hearts
let heartsColor = hearts.color()
print(heartsColor) // black      

注意,有两种方式可以引用Hearts 成员:给hearts 常量赋值时,枚举成员Suit.Hearts 需要用全名来引用,因为常量没有显式指定类型
在switch 里,枚举成员使用缩写.Hearts 来引用,因为self 的值已经知道是一个suit
已知变量类型的情况下你可以使用缩写。

• 协议和扩展(Protocols and Extensions) 

使用protocol 来声明一个协议
类、枚举和结构体都可以实现协议


protocol ExampleProtocol {
var simpleDescription :String {get}
mutating func adjust()
}
class SimpleClass :ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty :Int = 69105
func adjust() {
simpleDescription += "Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
print(aDescription) // A very simple class.Now 100% adjusted.
struct SimpleStructure :ExampleProtocol {
var simpleDescription: String = "A simple struture"
mutating func adjust(){
simpleDescription += "(adjusted)"

}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
print(bDescription)  //A simple struture(adjusted)

注意声明 SimpleStructure 时候 mutating 关键字用来标记一个会修改结构体的方法
SimpleClass 的声明不需要 标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。
你可以像使用其他命名类型一样使用协议名——例如,创建一个有不同类型但是都实现一个协议的对象集合
当你处理类型是协议的值时,协议外定义的方法不可用


let protocalValue : ExampleProtocol = a
print(protocalValue.simpleDescription) //A very simple class.Now 100% adjusted.
print(protocalValue.anotherProperty) //Value of type ‘ExampleProtocal’ has no member ‘anotherProperty'

即使 protocolValue 变量运行时的类型是 simpleClass ,编译器会把它的类型当做 ExampleProtocol 。这表示你不能调用类在它实现的协议之外实现的方法或者属性。

• 错误处理(Error Handling) 
使用采用 Error 协议的类型来表示错误

enum PrinterError: Error {
case OutOfPaper
case NoToner
case OnFire }

使用 throw 来抛出一个错误并使用 throws 来表示一个可以抛出错误的函数
如果在函数中抛出一个错误,这个函 数会立刻返回并且调用该函数的代码会进行错误处理
有多种方式可以用来进行错误处理。一种方式是使用 do-catch 。在 do 代码块中,使用 try 来标记可以抛出错误 的代码。在 catch 代码块中,除非你另外命名,否则错误会自动命名为 error 。
可以使用多个 catch 块来处理特定的错误。

do{
let printerResponse = try sendToPrinter("Never Has Toner")
print(printerResponse)
}catch PrinterError .OnFire {
print("I'll just put this over here,with the rest of the fire")
}catch let printerError as PrinterError {
print("Printer error :\(printerError).")
}catch {
print(error)
}
另一种处理错误的方式使用try? 将结果转换为可选的
如果函数抛出错误,该错误会被抛弃并且结果为ni l
否则的话,结果会是一个包含函数返回值的可选值

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")
使用defer 代码块来表示在函数返回前,函数中最后执行的代码
无论函数是否会抛出错误,这段代码都将执
使用defer ,可以把函数调用之初就要执行的代码和函数调用结束时的扫尾代码写在一起,虽然这两者的执 行时机截然不同

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
let res = fridgeContains("banana")// 这里如果没有进行赋值 运算 会出现 没有被使用的警告
print(res) //因为“banana”不在数组中,所以这里的结果是false 如果是在数组中的值会打印出 true

• 泛型(Generics) 
尖括号里写一个名字来创建一个泛型函数或者类型

func repeatItem<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes { // 这里的 _ 是一个占位符 【意思大概是  _ 循环了后面0..<num 的次数,而对 _ 代表的意义并不关心】
result.append(item)
}
return result
}
let res = repeatItem(repeating: "knock", numberOfTimes:4) //Item 是泛型 所以repeating 的内容 不确定 可以是字符串 或者 整型等
print(res)// ["knock", "knock", "knock", "knock"]
可以创建泛型函数、方法、类、枚举和结构体

// 重新实现 Swift 标准库中的可选类型
enum OptionalValue<Wrapped> {
case None
case Some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
print(possibleInteger) //Some(100)
在类型名后面使用where 来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者 限定某个类必须有一个特定的父类 //例子不知是什么问题。

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element { //这里有问题。
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])
<T: Equatable> 和<T> ... where T: Equatable> 是等价的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值