Swift 语法浅聊

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,但比它功能更强大。

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

推荐阅读更多精彩内容