IOS APP开发学习记录--第0篇(语言篇)

IOS app开发学习记录–第0篇(语言篇)

写在前面

刚刚考研结束,终于可以可以做自己喜欢做想做的事情了,想想大学四年来学到的东西不少,但零零散散的缺乏记录和总结,所以觉得应该找个方式记录下来,就开始写博客,希望能以这种方式记录我的学习进步过程,也希望能帮助其他人避开一些坑。

关于ios开发

ios开发只是这一学期选的一门选修课,但因为一学期一直在准备考研,所以一直也没怎么去听课,所以ios基础可以说从零开始,所以这一系列应该只是简单的新手教程,大部分的开发过程的学习都是直接从苹果官网上翻译过来的,所以,所以除了一些个人的体会以外的开发部份相关内容都,不是原创!不是原创!不是原创!

本篇内容

内容适用人群:有最最最基础的编程知识即可
PS:只适合ios开发新手,大佬请绕道

本篇主要用于介绍一下ios开发语言:Swift语言
必要时可以点到原文下载官网提供的Playground用以练习,下载Playground的好处是能够随意修改原文给出的样例代码并能立刻得到结果反馈。
原文链接

正文

ios开发一开始使用的是object-c语言,后来升级为swift语言,个人认为swift语言可以说是我接触过语言中最友好的语言,语法上来说,比较接近javascript。

1.变量与常量定义

var用于变量定义,let用于常量定义

var myVariable = 42
myVariable = 50
let myConstant = 42

当没有指定变量类型时,编译器会根据初值判断变量类型,如上述类型都为int,当需要将手动确定变量类型时需要用":类型"来确定。

let myConstant: Double = 42

当需要转化变量类型时,必须显示声明,否则会报错

let label = "The width is "
let width = 94
let widthLabel = label + String(width)

当然,有更为简单的字符串拼接的方法

let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples." //等价于“I have 3 apples”
let fruitSummary = "I have \(apples + oranges) pieces of fruit."//等价于“I have 8 apples”

更友好的是,swift支持多行编辑

let quotation = """
Even though there's whitespace to the left,
the actual lines aren't indented.
   Except for this line.
Double quotes (") can appear without being escaped.

I still have \(apples + oranges) pieces of fruit.
    """

关于数组和字典的创建。

var shoppingList = ["catfish", "water", "tulips"]
shoppingList[1] = "bottle of water"

var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"//该行效果为为字典加入一组元素
occupations["Kaylee"] = "Public Relations"//该行效果为改变字典中关键字为“Kaylee”的一项的值

动态增加数组元素

shoppingList.append("blue paint")

空数组和字典的创建

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

当变量类型可以被推断出来(比如给一个类型确定的变量赋值,或函数传参),数组和字典的类型信息可以被省略,写成如下形式:

shoppingList = []
occupations = [:]
2.程序控制

程序流程控制部分与大部分流行的编程语言类似,以下只举例列出一些swift中比较特别的细节。
(1)判断语句
样例1:

var a=0
if a>0 {
	print(a)
}

第一点:无论条件语句还是循环语句,判断条件的括号是可选的,即可以写也可以不写,但是花括号是必须的必须写花括号。
第二点:判断条件必须是一个bool表达式,如样例中“a>0”不能写成"a",因为swift不支持隐式的转换,必须写成显式的形式。

样例2:

1.var optionalString: String? = "Hello"
2.print(optionalString == nil)
3.// Prints "false"
4.
5.var optionalName: String? = "John Appleseed"
6.var greeting = "Hello!"
7.if let name = optionalName {
8.    greeting = "Hello, \(name)"
9.}

首先要先认识swift语言中一种比较特别的类型:optional类型
optional类型指的是一个变量可能有值,或者可能为空,比如样例中“optionalString”在定义其类型时写成“String?”的形式,其中“?”就指明optionalString可能为空或可能有值。
这一类型的一个重要如样例中第7行所示,这一行表示,如果optional类型的变量optionalName不为空,就把其值赋给name并且执行if语句,否则跳过。

optional类型的另一用法是利用“??”操作符

let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"

上述代码第三行表示,如果nickName为空,则将“Hi John Appleseed”赋给informalGreeting,否则将"Hi"+nickName
赋给informalGreeting

样例3:

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.")
}

第一点,注意关键字"let"在各种判断,循环语句中的灵活应用,如“if let”,“for let”等等。
第二点,swift中的switch语句不需要在每一种情况后加"break",遇到匹配情况,执行完后会自动跳出。

(2)循环语句
样例1:

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)
// Prints "25"

swift中的"for in"语句等价于用for后面的变量遍历in后面的一组数据。
样例2:

var n = 2
while n < 100 {
    n *= 2
}
print(n)
// Prints "128"

var m = 2
repeat {
    m *= 2
} while m < 100
print(m)
// Prints "128"

while语句与repeat语句的用法如上
样例3:

var total = 0
for i in 0..<4 {
    total += i
}
print(total)

可以用“0…<4”来表示从0到3
用“0…<4”来表示从0到4
故样例中输出为6,若改为“0…<4”,则输出为10

3.函数和闭包

一个完整的函数定义:
func 函数名(函数外使用变量名1 函数内使用变量名1:类型1,……)->返回值类型{ 函数体 }
样例1:

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

样例1中有一个函数外变量名为“_”,表示该函数外变量名为默认值,则在调用该函数时这一个参数不需要加函数外变量名。
其中,函数内使用变量名也可以被省略如下,这时函数内变量名和函数外变量名相同(一般会使用这种写法)
样例2:

func greet(person: String, day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

注意这种情况下函数调用的时候不能省略函数外使用名。
样例3:

func calculateStatistics(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 = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
// Prints "120"
print(statistics.2)
// Prints "120"

当需要多返回值时,可按照样例3的方法构造返回值
样例4:函数捕获

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
returnFifteen()

函数捕获意味着,函数内部可以定义一个函数,这个函数可以使用其外层函数定义的变量,如样例4中,add函数中的y变量并非该函数内部的变量,而是函数外的变量。
样例5:函数作为返回值

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

样例5中返回值为一个函数,该函数带一个int类型的参数,该函数的返回值为int
样例6:函数作为参数

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(list: numbers, condition: lessThanTen)

样例6第一个函数的第二个参数为一个参数是int,返回值是bool的函数,第二个函数定义了一个判断数字是否小于10的函数,最后的函数调用将第二个函数作为参数传给第一个参数,用以判断list中是否存在小于10的数。
样例7:闭包

numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})

函数的实质其实就是一个带参数和返回值的代码块,当你需要调用的时候才会用到函数名,可是很多情况下我们并不需要很多次的使用该函数,只是需要将它作为某一次执行的参数,这个时候如果再为这一次执行写一个函数,每次执行的时候调用一次这个函数会浪费很多的栈空间,代码的复用率也不高,这个时候就不需要函数名,而是将代码打包成一个块,这就是函数闭包(在C++中叫匿名函数)
样例7中的numbers.map调用需要一个类型为函数的参数,这个时候可以用“{()}”来定义一个代码块,作为函数闭包传参,其中"(number:Int)"为参数,“->Int”为返回值,函数体与返回值之间用“in”分割。
当该闭包的参数类型和返回类型已知的时候(比如说在调用该闭包的函数定义中已经声明),则可以省略该闭包的参数和返回值的声明。如

let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)

甚至在某些比较短的闭包中,可以用序号代替参数名,如:

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

其中,“$0”代表第一个参数,“$1”代表第二个参数

4.类与对象

这一部分大部分也和主流面向对象的程序语言类似,只举例说明一些特殊的细节
样例1:类的创建和调用

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

构造函数:init(参数……)

样例2:子类的创建和函数覆盖

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 sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

用冒号分割子类名与父类名,覆盖父类中的函数在函数定义时加一个“override”前缀

样例3:属性的getter和setter

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }

    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
// Prints "9.3"
triangle.perimeter = 9.9
print(triangle.sideLength)
// Prints "3.3000000000000003"

注意每一属性的setter都有一个默认的参数“newValue”用以表示该属性要赋的值。
注意构造函数中的顺序,每一个子类的定义都要遵循这一顺序:
首先初始化子类中特有的变量
再调用父类的构造函数
再更改父类的某些属性

5.枚举类型和结构体

样例1:

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    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

枚举类型如果没有设定,则每一个变量的值从0开始每次增1,样例中ace到king会是0到12,但样例中ace赋值为1,故实际上为1到13。

样例2:

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

结构体和类基本上没有区别,他们唯一的区别在于,在传参时结构体时值传递,而类传的是引用(这一点如果不理解可以看看其他相关资料)

6.协议与扩展

样例1:

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

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

第一,协议也是一种类,用于规范其他的类的行为,用关键字“protocol”来创建
第二,继承协议的子类或者结构体必须完善协议中规定的方法。
第三,对样例中关键字“mutating”的说明,在上一部分说过结构体与类之间的区别是值传递和引用传递,那么如果是一个结构体继承了某一协议,那么在覆盖其方法的过程中需要改变内部属性的值,但因为结构体是值传递所以无法改变,这时就可以用“mutating”前缀来实现改变属性的值。所以可以注意到在类中覆盖协议中的方法并没有使用mutating关键字。

样例2:关于类扩展

extension Int: ExampleProtocol {
    var simpleDescription: String {
        return "The number \(self)"
    }
    mutating func adjust() {
        self += 42
    }
}
print(7.simpleDescription)
// Prints "The number 7"

可以用extension来对某一已经存在的类或协议或结构体等类型添加方法或属性。

7.异常处理

样例1:

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

可以继承"Error"协议来定义一个异常

样例2:

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

发生异常时实用关键字“throw”来抛出异常,异常抛出后,该函数立刻结束,返回调用该函数的函数来进行异常处理,会抛出异常的函数在定义时要加“throws”关键字

样例3:

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    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)
}

第一,在调用可能抛出异常的函数时,将可能抛出异常的函数用do-catch结构写出来。
第二,可能抛出异常的函数调用前加关键字“try”.

对于异常的处理,除了do-catch还有另一种处理方式

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")

利用“try?”,若函数发生异常,则异常被抛弃,printerSuccess值为空,若正常,则printerSuccess值为返回结果。

你可能感兴趣的:(文档翻译)