Swift学习笔记

一、Swift预览


1.1 简单值


let作为常量  var作为变量,常量只有在定义时赋值一次,可以多次使用。如

let myConstant = 79   //常量

var myVariable = 80   //变量

常量和变量在设定值时必须采用相同格式。但并不需要定义精准的类型。创建一个常量或变量时提供一个值,让编译器判断其类型。在上面的例子中,编译器指定 myVariable 是一个整数,因为它的初始值是整数

若初始化时未提供足够信息(没有初始值),可以在变量后面指定类型,用冒号隔开。

let myConstant1 = 79

let myConstant2 = 80.0

let myConstant3 : Double = 81.0

值在转化为另一种类型时从不具有隐含性。如果需要转化值到另一种类型,请明确性地为值进行格式转换。

let label = "The width is "

let width = 94

let widthLabel = label + String(width)

更简单的方法将值转换为String:将值写在括号中,并在括号前添加一个反斜杠。例

let apples = 3

let oranges = 5

let appleSummary = "I have \(apples) apples."

let fruitSummary = "I have \(apples + oranges) pieces of fruit."

通过 [] 创建一个数组和字典,通过 index 和 key 获取对应的值

var shoppingList = ["catfish", "water", "tulips", "blue paint"]

shoppingList[1] = "bottle of water"

var occupations = [

"Malcolm": "Captain",

"Kaylee": "Mechanic",

]

occupations["Jayne"] = "Public Relations"

创建空数组和字典,以及初始化语法。

let emptyArray = String[]()

let emptyDictionary = Dictionary()

为了防止类型信息被更改,空数组列用[],空字典用[:]进行初始化 - 例如,为变量赋新值和给函数传递参数的时候。

shoppingList = []


1.2 流程控制


使用 if 和 switch 判断条件,使用 for-in 、 for 、 while 和 do-while 处理循环。条件和循环变量的括号可以省略,语句体的大括号是必须的。

let personalScore = [90,129,49,28,109]

var teamScore = 0

for score in personalScore{

        if score > 60 {

            teamScore += 3

        }else{

            teamScore += 1

        }

}

在 if 语句中,条件必须是一个布尔表达式 —— 这意味着像 if score { ... } 这样的代码将报错,而不会隐形地与 0 做对比。有些变量的值是可选的。一个可选的值如果是一个具体的值或者是 nil ,那表明这个值缺失。在类型后面加一个 ? 来标记这个变量的值是可选的。

var testName: NSString? = ""

testName = nil

if  let name = testName {

    print("success \(name)")

}else{

    print("what the fuck, guy")

}

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


使用switch 支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。

let vegetable = "西红柿"

switch vegetable {

case "土豆":

    print( "我就是土豆.")

case "油麦", "苦菊":

    print("我就是绿叶菜.")

case let x where x.hasSuffix("土"):

    print( "\(x) 我就是土里的")

default:

    print("我不在你的菜篮子里")

}


可以在循环中使用 ..< 和 ... 来表示范围

var total = 0

// ..< 即 0到100 不包括100    ... 即0到100 包括100

for i in 0..<100 {

    if i % 2 == 0 {

        total += i

    }

}

print("100 以内的偶数的和为\(total)")


1.3 函数与闭包


使用 func 来声明一个函数,通过函数的名字和参数来调用函数。使用 -> 指定函数返回值(分离了返回值和参数)

func getMaxValue(num1 : Int , num2 : Int) -> Int{

    if num1 > num2 {

        return num1

    }else{

        return num2

    }

}

var max = getMaxValue(num1: 100, num2: 120)

print("最大值为\(max)")

返回值为元祖

func getVegetablesPrice()->( Double , Double){

    return (3.35,4.35)

}

var (price1,price2) = getVegetablesPrice()

print("price1 = \(price1)  price2 = \(price2)")

//或者

var prices = getVegetablesPrice()

print("price1 = \(prices.0)  price2 = \(prices.1)")


1.4 对象与类


通过类名和 () 创建一个类的实例,实例通过点语法访问属性和方法。

var shape = Shape()

shape.numberOfSides = 7

var shapeDescription = shape.simpleDescription()

如果子类需要重写父类的方法,使用 override 关键字;如果没有使用 override 就重写父类的方法,编译器将会报错。同样的编译器会检查 override 是否在父类中。

为了更简单的存储属性,属性可以有 setter 和 getter 方法

var shapeLength : Int{

    get{

        return shapeLength

    }

    set{

        if newValue < 0  {

            shapeLength = 0

        }else{

            shapeLength = newValue

        }

    }

}

在shapeLength的 setter 中,新值有一个隐式的名称是newValue。可以在set之后的括号中提供一个明确的名称.如果不需要计算属性值,但是需要在设置新值之前或者之后执行一些代码,可以通过 willSet 和 didSet 完成

使用set 必须要用get 但是get 可以单独使用(只有get的叫只读计算属性)

属性改变之前触发willSet方法,属性改变之后触发didSet方法

在给属性添加观察者之前必须要明确申明属性的类型,否则编译器会报错

属性初始化时,willSet和didSet都不会调用,只有在设置属性值时才会调用

当设置的值和原来的值一样时,willSet和didSet也会被调用

willSet有一个newValue参数,,,didSet有一个newValue参数


常量和变量的命名



可以用任何喜欢的字符作为常量和变量名,包括 Unicode 字符:

let π = 3.14159

let 你好 = "你好世界"

let 🐶🐮 = "dogcow"

常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。

一旦将常量或者变量声明为确定的类型,就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,也不能将常量与变量进行互转。

注意:

如果需要使用与Swift保留关键字相同的名称作为常量或者变量名,可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,应当避免使用关键字作为常量或变量名,除非别无选择。

可以更改现有的变量值为其他同类型的值,在下面的例子中,friendlyWelcome的值从"Hello!"改为了"Bonjour!":

var friendlyWelcome = "Hello!"

friendlyWelcome = "Bonjour!"

// friendlyWelcome 现在是 "Bonjour!"

与变量不同,常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错:

let languageName = "Swift"

languageName = "Swift++"

// 这会报编译时错误 - languageName 不可改变


输出常量和变量


可以用print(_:separator:terminator:)函数来输出当前常量或变量的值:

print(friendlyWelcome)

// 输出 "Bonjour!"

print(_:separator:terminator:) 是一个用来输出一个或多个值到适当输出区的全局函数。如果用 Xcode,print(_:separator:terminator:) 将会输出内容到“console”面板上。separator 和 terminator 参数具有默认值,因此调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 terminator 参数--例如,print(someValue, terminator:"") 。关于参数默认值的更多信息,请参考默认参数值。

Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:

print("The current value of friendlyWelcome is \(friendlyWelcome)")

// 输出 "The current value of friendlyWelcome is Bonjour!

注意:

字符串插值所有可用的选项,请参考字符串插值。


分号


与其他大部分编程语言不同,Swift 并不强制要求在每条语句的结尾处使用分号(;),当然,也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即打算在同一行内写多条独立的语句:

let cat = "🐱"; print(cat)

// 输出 "🐱"


类型别名


类型别名(type aliases)就是给现有类型定义另一个名字。可以使用typealias关键字来定义类型别名。

当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设正在处理特定长度的外部资源的数据:

typealias AudioSample = UInt16

定义了一个类型别名之后,可以在任何使用原始名的地方使用别名:

var maxAmplitudeFound = AudioSample.min

// maxAmplitudeFound 现在是 0

本例中,AudioSample被定义为UInt16的一个别名。因为它是别名,AudioSample.min实际上是UInt16.min,所以会给maxAmplitudeFound赋一个初值0。


可选类型


使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示:

有值,等于 x

或者

没有值

注意:

C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回nil,nil表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如NSNotFound)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示任意类型的值缺失,并不需要一个特殊值。

来看一个例子。Swift 的 Int 类型有一种构造器,作用是将一个 String 值转换成一个 Int 值。然而,并不是所有的字符串都可以转换成一个整数。字符串 "123" 可以被转换成数字 123 ,但是字符串 "hello, world" 不行。

下面的例子使用这种构造器来尝试将一个 String 转换成 Int:

let possibleNumber = "123"

let convertedNumber = Int(possibleNumber)

// convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"

因为该构造器可能会失败,所以它返回一个可选类型(optional)Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值。(不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都没有。)


nil


可以给可选变量赋值为nil来表示它没有值:

var serverResponseCode: Int? = 404

// serverResponseCode 包含一个可选的 Int 值 404

serverResponseCode = nil

// serverResponseCode 现在不包含值

注意:

nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。

如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil:

var surveyAnswer: String?

// surveyAnswer 被自动设置为 nil

注意:

Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。


空合运算符


空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。

空合运算符是对以下代码的简短表达方法:

a != nil ? a! : b

上述代码使用了三目运算符。当可选类型 a 的值不为空时,进行强制解封(a!),访问 a 中的值;反之返回默认值 b。无疑空合运算符(??)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。

注意: 如果 a 为非空值(non-nil),那么值 b 将不会被计算。这也就是所谓的短路求值。

下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:

let defaultColorName = "red"

var userDefinedColorName: String?  //默认值为 nil

var colorNameToUse = userDefinedColorName ?? defaultColorName

// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"

userDefinedColorName 变量被定义为一个可选的 String 类型,默认值为 nil。由于 userDefinedColorName 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 colorNameToUse 的变量赋予一个字符串类型初始值。 由于 userDefinedColorName 值为空,因此表达式 userDefinedColorName ?? defaultColorName 返回 defaultColorName 的值,即 red。

另一种情况,分配一个非空值(non-nil)给 userDefinedColorName,再次执行空合运算,运算结果为封包在 userDefaultColorName 中的值,而非默认值。

userDefinedColorName = "green"

colorNameToUse = userDefinedColorName ?? defaultColorName

// userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"


区间运算符


闭区间运算符

闭区间运算符(a...b)定义一个包含从 a 到 b(包括 a 和 b)的所有值的区间。a 的值不能超过 b。 ‌ 闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 for-in 循环中:

for index in 1...5 {

print("\(index) * 5 = \(index * 5)")

}

// 1 * 5 = 5

// 2 * 5 = 10

// 3 * 5 = 15

// 4 * 5 = 20

// 5 * 5 = 25

半开区间运算符

半开区间(a..<b)定义一个从 a 到 b 但不包括 b 的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。

半开区间的实用性在于当你使用一个从 0 开始的列表(如数组)时,非常方便地从0数到列表的长度。

let names = ["Anna", "Alex", "Brian", "Jack"]

let count = names.count

for i in 0..<count {

print("第 \(i + 1) 个人叫 \(names[i])")

}

// 第 1 个人叫 Anna

// 第 2 个人叫 Alex

// 第 3 个人叫 Brian

// 第 4 个人叫 Jack


字符串索引


每一个String值都有一个关联的索引(index)类型,String.Index,它对应着字符串中的每一个Character的位置。

前面提到,不同的字符可能会占用不同数量的内存空间,所以要知道Character的确定位置,就必须从String开头遍历每一个 Unicode 标量直到结尾。因此,Swift 的字符串不能用整数(integer)做索引。

使用startIndex属性可以获取一个String的第一个Character的索引。使用endIndex属性可以获取最后一个Character的后一个位置的索引。因此,endIndex属性不能作为一个字符串的有效下标。如果String是空串,startIndex和endIndex是相等的。

通过调用 String 的 index(before:) 或 index(after:) 方法,可以立即得到前面或后面的一个索引。您还可以通过调用 index(_:offsetBy:) 方法来获取对应偏移量的索引,这种方式可以避免多次调用 index(before:) 或 index(after:) 方法。

你可以使用下标语法来访问 String 特定索引的 Character。

let greeting = "Guten Tag!"

greeting[greeting.startIndex]

// G

greeting[greeting.index(before: greeting.endIndex)]

// !

greeting[greeting.index(after: greeting.startIndex)]

// u

let index = greeting.index(greeting.startIndex, offsetBy: 7)

greeting[index]

// a

试图获取越界索引对应的 Character,将引发一个运行时错误。

greeting[greeting.endIndex] // error

greeting.index(after: endIndex) // error

使用 characters 属性的 indices 属性会创建一个包含全部索引的范围(Range),用来在一个字符串中访问单个字符。

for index in greeting.characters.indices {

print("\(greeting[index]) ", terminator: "")

}

// 打印输出 "G u t e n  T a g ! "

注意: 可以使用 startIndex 和 endIndex 属性或者 index(before:) 、index(after:) 和 index(_:offsetBy:) 方法在任意一个确认的并遵循 Collection 协议的类型里面,如上文所示是使用在 String 中,也可以使用在 Array、Dictionary 和 Set中。


插入和删除


调用 insert(_:atIndex:) 方法可以在一个字符串的指定索引插入一个字符,调用 insert(contentsOf:at:) 方法可以在一个字符串的指定索引插入一个段字符串。

var welcome = "hello"

welcome.insert("!", at: welcome.endIndex)

// welcome 变量现在等于 "hello!"

welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex))

// welcome 变量现在等于 "hello there!"

调用 remove(at:) 方法可以在一个字符串的指定索引删除一个字符,调用 removeSubrange(_:) 方法可以在一个字符串的指定索引删除一个子字符串。

welcome.remove(at: welcome.index(before: welcome.endIndex))

// welcome 现在等于 "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex

welcome.removeSubrange(range)

// welcome 现在等于 "hello"

注意: 可以使用 insert(_:at:)、insert(contentsOf:at:)、remove(at:) 和 removeSubrange(_:) 方法在任意一个确认的并遵循 RangeReplaceableCollection 协议的类型里面,如上文所示是使用在 String 中,也可以使用在 Array、Dictionary 和 Set 中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容