Swift 语法浅聊
1. 简单值
使用** let 来声明常量,使用 var **来声明变量.
简单值数据类型要首字母大写.
var zl = 42
let zl = 24
let zl: Double = 100
2.类型转换
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换:
字符串拼接用加号+,代表着转换成字符串类型的。
转换成什么类型的就放在前面。
let label = "The width is"
let width = 94
let widthLabel = label + String(width)
3.字符串拼接
有一种更简单的把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠。例如:
swift中并无占位符,用** \() **来表示。
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit.”
4.定义数组 和 字典
使用方括号[]来创建数组和字典,并使用下标或者键(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 = [String: Float]()
PS:String:表示数组元素类型,数据类型首字母要大写
():表示构造器,创建一个实例对象
Swift中两个数组可用加号+直接相加,拼接成一个数组;而OC中不能数组直接相加。
String:key类型
Float:value类型
Swift中key可以是任意类型,而OC中必须是字符串类型。
如果类型信息可以被推断出来,你可以用[]和[:]来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样。
shoppingList = []
occupations = [:]
5.循环 Control Flow控制流
使用if和switch来进行条件操作,使用for-in、for、while和repeat-while来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
PS:
1、 只要变量名score,并不写数据类型,因为数组里能推断出变量类型。
2、 if的判断条件在Swift中不加括号了,
6.Switch-case分支语句
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.")
PS:
1、每条分支语句不用写break语句,运行switch中匹配到的子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break。
2、case中条件不一定是确定值,可以是一个取值范围,注意let在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量x。
3、分支中可以加判断条件
4、where是判断条件的关键字
7.Bool值
在if语句中,条件必须是一个布尔表达式——这意味着像if score { ... }这样的代码将报错,而不会隐形地与 0 做对比。
if中Bool值判断条件必须补全,不能用==nil或者!;
Swift中Bool值与OC中不同,只有true/false;OC中Bool值Yes代表非0,NO代表0.
8.区间
你可以在循环中使用** ..< **来表示范围,也可以使用传统的写法,两者是等价的:
var firstForLoop = 0
for i in 0..<4 {
firstForLoop += i
}
print(firstForLoop)
var secondForLoop = 0
for var i = 0; i < 4; ++i {
secondForLoop += i
}
print(secondForLoop)
使用..<创建的范围不包含上界,如果想包含的话需要使用 ... 。
0..<4 -> 代表 [0, 4)
0...4 -> 代表 [0, 4]
9.可选值
一个可选的值是一个具体的值或者是nil以表示值缺失。在类型后面加一个问号?来标记这个变量的值是可选的。
在变量类型名后加问号?,可选值仅仅针对数据类型而已。可分为空值类型nil和非空类型。
var optionalString: String? = "Hello”
Swift中只有可选值为空,空是一个明确值,这个值是nil。
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?。如果?之前的值是nil,?后面的东西都会被忽略,并且整个表达式返回nil。否则,?之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
10.解包与闭包
在变量名后面加一个感叹号!
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
等价于
if optionalName != nil {
greeting = "Hello, \(name)"
}
解包:把可选去掉,从而确定类型
name!解包;而String! —> 自动解包
可选绑定:
*1 可选值自动解包
*2 解包出来不是nil空值,才会走if语句
闭包:相当于OC中的block,在Swift中闭包比block强大,可以传函数.
11.元组
是数据类型(类型中可以有一组数据),元组中数据可以是不同数据类型。
(min: Int, max: Int, name: String)
12.函数
使用* func 来声明一个函数,使用名字和参数来调用函数。使用 -> *来指定函数返回值的类型。
使用元组来让一个函数返回多个值。该元组的元素可以用名称或数字来表示。
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([5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)
PS:OC中如果要返回多个值,第一个是return返回带回;第二个是指针带回(传参后指针赋值);
而Swift中用元组去返回多个值
func sumOf(numbers:Int… )->Int
其中Int…代表一组同类型数据的数组
函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式:
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(42, 597, 12)
函数可以嵌套。被嵌套的函数可以访问外侧函数的变量,你可以使用嵌套函数来重构一个太长或者太复杂的函数。
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
函数是第一等类型,这意味着函数可以作为另一个函数的返回值。
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, condition: lessThanTen
PS:Swift中函数可以嵌套,OC中函数可以调用。
函数可作返回值(仅在意返回值数据类型)使用,也可作参数传入另一个函数(仅在意参数数据类型)使用
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到。你可以使用{}来创建一个匿名闭包。使用in将参数和返回值类型声明与闭包函数体进行分离。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
13.对象和类
使用* class *和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。同样,方法和函数声明也一样。
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
PS:Swift中构造器():创建新对象,
shape:实例对象,
Shape():类名,
()无参的构造函数,
var shape = Shape()
要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。
PS:Swift中属性必须初始化,可有默认值
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
这个版本的Shape类缺少了一些重要的东西:一个构造函数来初始化类实例。使用init来创建一个构造器。
PS:当类中属性无默认值(var name: String),则需要使用* init() *来创建一个构造器。
如果你需要在删除对象之前进行一些清理工作,使用* deinit *创建一个析构函数。
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
注意self被用来区别实例变量。当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要赋值——无论是通过声明(就像numberOfSides)还是通过构造器(就像name)。
14.继承
子类的定义方法是在它们的类名后面加上父类的名字(左边子类名继承于右边父类名,父类名要首字母大写),用冒号分割。创建类的时候并不需要一个标准的根类,所以你可以忽略父类。
子类如果要重写父类的方法的话,需要用* override *标记——如果没有添加override就重写父类方法的话编译器会报错。编译器同样会检测override标记的方法是否确实在父类中。
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()
PS:Swift中在子类构造器中先初始化子类中特有的属性方法,然后再去调用父类的构造器init(); 而OC中重写父类方法,需要先调用父类[super …],在去扩展子类属性方法。
15.属性
分为两类:*1 计算属性:关键字是set/get ; *2 存储属性:普通声明属性var width: Double = 0.0
- 总结:* 只读参数时,只有get方法取值,无set方法改值存值。
get是必有的,set可有可无,但有set后必有get。
当无get/set,则是存储属性。
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 triagle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
在perimeter的 setter 中,新值的名字是newValue。你可以在set之后显式的设置一个名字。
注意EquilateralTriangle类的构造器执行了三步:
*1 设置子类声明的属性值
*2 调用父类的构造器
*3 改变父类定义的属性值。其他的工作比如调用方法、getters和setters也可以在这个阶段完成。
** 属性监视器:**
*1 如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSet{}和didSet{}。
*2 设置默认值时就是存储属性,无需做set方法改值存值,则也不会调用willSet{}和didSet{}。当存储属性有默认值后第一次不去调用willSet{}和didSet{}方法,当它一旦改值使用set方法,就会调用调用willSet{}和didSet{}。
*3 willSet{}、didSet{} 和set、get不能共存。
*4 用didSet{} 用时nil空值会报错,尽量用willSet{}。
懒加载中lazy:
lazy var topics: NSMutableArray? = {
// 这里面进行初始化配置
}()
** 注意:** 大括号{}后有个()
1 计算属性的值是不同的,是计算得来的,所以只能使用* var *来修饰,不能使用let。
2 计算属性本身是不能直接复制的,他是通过其他的变量/常量来计算结果得到的数据。这个时候不可用成员变量_x的,Swift中无成员变量。
16.枚举
使用关键字* enum *来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法。值类型用assign
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
在上面的例子中,枚举原始值的类型是Int,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用rawValue属性来访问一个枚举成员的原始值。
PS:枚举名要首字母大写。
枚举内枚举值需用* case *开头。
枚举内可定义方法。
使用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"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
17.结构体
使用关键字* struct *来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是结构体是传值,类是传引用。
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()
PS:同样可内部定义一个函数,OC中均不可。定义枚举的时候设置原始值。
结构体和类的区别:
在Swift中,
*1 类可以继承:引用类型(类引用—> 指针指向它,地址拷贝);而结构体不可继承:值类型(赋值使用—>对象拷贝)。
*2 类可以选择部分属性,而结构体中属性必须全部逐一构造器。
import UIKit
var str = "Hello, playground"
// 引用类型
class Person {
var age: Int!
}
var p = Person()
p.age = 20
let p2 = Person()
p2.age = 10
p = p2 // 引用:p指向p2地址
p.age // 10
p2.age // 10
p.age = 30 // 同一个地址,地址里内容改变
p2.age // 30
// 值类型
struct Name {
var name: String?
}
var n = Name(name: "zl")
let n2 = Name(name: "dz")
n = n2 // 赋值:把n2的值赋给n
n.name // dz
n.name = "as" // Name
n2.name // dz
18.协议与扩展
协议:使用* protocol *来声明一个协议。
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
类、枚举和结构体都可以实现协议。
Swift中可以定义属性且不生成成员变量;OC中只定义方法,协议不能定义属性。
扩展:使用* extension *来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
Swift中扩展extension相当于OC中的分类category,但比它功能更强大。