Swift4 学习笔记——基础篇

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览

语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")
print("world")

print("hello"); print("world")

print("hello");
print("world");

print(
    "world")

注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

  • struct, Bool,Int, Double, String, Array,Dictionary等
  • enum, Optional类型
  • class, 用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

  • protocol,ExpressibleByIntegerLiteral

  • tuple,函数返回多值时使用。
  • function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categories: first, “value types”, where each instance keeps a unique copy of its data, usually defined as a struct, enum, or tuple. The second, “reference types”, where instances share a single copy of the data, and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型

变量与常量

let one = 1
var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

  • 只声明,不初始化。
var x : Int
  • 想要的类型和推断的类型不符合
let separator : CGFloat = 2.0
  • 不能推断出类型
let opts : UIViewAnimationOptions = [.autoreverse, .repeat]
  • 还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration

基本类型用法

Bool
  • Bool是一个struct类型
  • 只有true和false两个值,不能做它解释。
Int
  • Int是struct类型
  • Int的取值在Int.max和Int.min之间,平台相关
Double
  • Double是struct类型
  • 64位架构处理器上,Double的精度是15位
  • Double的边界是Double.infinity,还有Double.pi等
  • 使用isZero来判断Double是否为0
数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10
let d : Double = i // compile error

正确的写法是:

let i = 10
let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {    
    let s = """    
    Line 1        
        Line 2    
    Line 3    
    """    
    // ...
}

func f() {    
    let s = """
    Line "1"        
        Line 2 \    
    and this is still Line 2    
    """    
    // ...
}

在String字面变量中使用(…)来计算表达式

let n = 5
let s = "You have \(n) widgets."

String支持+号和+=号

let s = "hello"
let s2 = " world"
let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {   
    print(i) // 194, 191, 81, 117, 105, 195, 169, 110, 63

}

String和数值的转化:

let i = 7
let s = String(i) // "7"

let i = 31
let s = String(i, radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a, b] a..< b表示区间[a, b)

最常见的就是在for循环中使用:

for ix in 1...3 {    
    print(ix) // 1, then 2, then 3
}

Range 有实例方法:

let ix = // ... an Int ...
if (1...3).contains(ix) { // ...

let s = "hello"
let ix2 = s.index(before: s.endIndex)
let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int, String)

初始化:

var pair : (Int, String) = (1, "Two")
var pair = (1, "Two")

tuple可以同时给多个变量赋值:

let ix: Int
let s: String
(ix, s) = (1, "Two")

tuple在for-in中的应用:

let s = "hello"
for (ix,c) in s.enumerated() {
    print("character \(ix) is \(c)")

}

对Tuple中值的引用:

let pair = (1, "Two")
let ix = pair.0 // now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int, second:String) = (1, "Two")
//or: let pair = (first:1, second:"Two")

var pair = (first:1, second:"Two")
let x = pair.first // 1
pair.first = 2
let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int, y:Int)

func piece(at p:Point) -> Piece? {    
    let (i,j) = p    
    // ... error-checking goes here ...    
    return self.grid[i][j]

}

可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"
print(stringMaybe) // Optional("Howdy")
if stringMaybe == nil {    
    print("it is empty") // does not print
}
stringMaybe = nilprint(stringMaybe) // nil
if stringMaybe == nil {    
    print("it is empty") // prints
}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"
let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"
let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}
var stringMaybe : String! = "howdy"
realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.view是一个UIView!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"
var anotherStr = stringMaybe //ok
var pureStr: String = stringMaybe //ok
var errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?
// ... stringMaybe might be assigned a real value here ...
let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值