Swift学习笔记

基本语法

注释

// 单行注释
/* */ 多行注释
注:swift的多行注释支持嵌套,即/* /* */ */

语句

swift不强制要求句末使用分号;但是对于一行书写两条语句的情况,要使用分号分隔

变量、变量

标示符:以字符、下划线、美元符开头,后接任意数量的字符、数字、下划线、美元符
声明:(变量用var声明,常量用let声明)

var varName [:Type] [= Value]
例如:var b : Int 
     var name = "jackgxc.com" // 编译器会自动推断name变量为String类型

输出

print() println()函数,后者会在输出后加上换行符
在字符串中嵌套变量的时候使用\()形式

例如:

print("my name is \(myName)") // 其中myName是变量名

类型转换

newType(varName)

例如:

Int(myNumber) // 将myNumber变量转换为整型

元组(tuple)

  • 元组使用圆括号把多个值组合成一个复合值,元组内的值可使用任意类型,元组并不要求元组内的值具有相同的类型。
  • 定义:

    例如:
    var health = (180, 70, "Good")
    var score : (String, Int, String, Int)
    score = ("Chinese", 100, "Maths", 99)

  • 元组的成员可以是元组
  • 获取元组中的元素值可以通过下标,下表从0开始,使用.操作符

    例如:
    println(health.0) // 180

  • 元组的拆分

    例如:
    var (height, weight, status) = health
    println(height) // 180

  • 为元组中的元素命名

    例如:
    var health = (height: 180, weight: 70, status: "Good")
    println(health.status) // Good

可选类型

对于可能取值为nil的变量,建议使用可选类型,即在声明的时候加上一个?
(只有可选类型的变量或常量才能接受nil,非可选类型的变量和常量不能接受nil)

例如:

var num : Int?
num = nil // 不会编译错误

var anotherNum : Int
num = nil // 编译错误

程序不能直接把Int?类型的变量或常量当成Int类型使用,应该在后面添加!号进行强制解析,强制解析前应确保变量或常量的取值不为nil

例如:

var str : String = “jackgxc.com”
var i : Int? = str.toInt()
if i != nil
{
    var i1 : Int = i! // 使用!号进行强制解析
}

可选绑定

var str : String! = "jackgxc.com"
if var tmp = str // 判断str是否为nil,如果不为nil,将强制解析并赋值给tmp
{
    println(tmp)
}
else
{
    println("str is nil!")
}

隐式可选类型

Int Int? Int!
是否可储存整数 Y Y Y
是否接受nil N Y Y
是否需要强制解析为Int N Y N

类型别名

typealias newName = oldName
例如:
typealias Counter = Int
var i,j : Counter

字符串的遍历操作
-

for elements in str
{
    // ...
}

运算符

因大部分运算符与常用语言较为类似,故之列出一些特殊运算符。

溢出运算符(会将超过该数据类型有效位的部分直接舍弃)

  • &+ 溢出加
  • &- 溢出减
  • &* 溢出乘
  • &/ 溢出除
  • &% 溢出求余

范围运算符

闭范围:
a...b表示从a到b的范围(包括a、b边界值)

半开范围:
a..<b表示从a到b的范围(包括a,但不包含b)

比较运算符

=== 恒等于,用于判断引用类型的变量,只有当两个引用变量都引用同一个对象时才返回true
!== 不恒等于,同上

逻辑运算符

&& 短路与
& 不短路与
|| 短路或
| 不短路或
^ 异或

nil合并运算符
-
a ?? b 判断可选类型变量a是否为nil,如果a不为nil,会返回a的实际值,否则返回b


流程控制
=
大部分的语句和常用语言也较为类似,只列出swift特色的语句。

switch分支语句
-

switch EXPRESSION
{
    case value1:
        statement(s)
    case value2,value3:
        statement(s)
    default:
        statement(s)
}
  • switch语句是完备的,所有可能出现的值都需要相应的case语句
  • 贯穿问题:swift不存在隐式贯穿,即默认只会执行一个statement(s),如果需要继续执行的话,请显式写明fallthrough,fallthrough贯穿不会检查后面case块的匹配条件,将会直接贯穿到下一个case块的执行代码
  • break语句:如果不想让某个块执行,则可以用break语句填充在statement部分
  • 范围匹配:case块后可以是一个范围,比如91...100
  • 元组匹配:支持使用元组匹配多个值;还可以在范围中使用下划线来匹配所有可能的值
  • 值绑定:case let/var varName:case (var x, 0):
  • case的where子句:case var(x, y) where x>0 && y>0:

for-in循环语句:
-

for 常量名|_ in 字符串|范围|集合
{
    statements
}
  • 如果不需要访问遍历的元素,可用_代替常量名

集合
=
数组
-

  • 创建:
    var myArr : Array<String> // 泛型语法
    var nums : [Int] // 简化语法
  • 使用Array的构造器创建数组:
    init( ) // 创建一个空数组
    init(count: ,repeatedVaule: ) // 创建一个包含了count个repeatedValue元素的数组

    例如:
    myArray = Array<String>()
    myArray = Array<String>(count: 10, repeatedValue: "jackgxc")
    var myArray = ["a","b","c"]

  • 添加元素:
    append(newElement:T)
    例如:
    var languages = ["swift"]
    languages.append("OC")
    println(languages) // swift, OC

    +
    例如:
    languages += ["PHP"]

  • 插入元素:
    insert(newElement:T, atIndex:Int)
  • 范围:
    例如:

languages[1...3] // 获取第2-4个元素
languages[0..<2] = ["Ruby"] // 将第1-2个元素替换为Ruby

  • 删除元素:
    removeAtIndex(index: Int)->T // 删除指定位置的元素,并返回待删除的元素

removeLast()->T // 删除最后一个元素,并返回待删除的元素
removeAll(keepCapacity: Bool = false) // 清空数组,默认不保留数据缓冲

  • 多维数组:
    以创建三维数组为例:

var 变量名 : [[[类型]]]
var 变量名 : Array<Array<Array<类型>>>

字典
-

  • 声明与创建:
  1. myDict : Dictionary<String, String> // 泛型

var scores : [String, Int]

  • 使用构造器:
    init(minimumCapacity : = 2)

例如:
myDict = Dictionary<String, String>()
score = Dictionary<String, Int>(minimumCapacity:5)

  • 简化语法:
  1. = ["身高": 170, “体重”: 74]

var emptyDict : [String:Double] = [:]

  • 更改value值
    updateValue(_: ,forKey:)->ValueType?
  • for-in 遍历
    例如:

for (season, desc) in seasons
{

...

}

  • 单独使用keys或values
    例如:

var keys = Array(season.keys) // 将所有的key包装成Array集合
var values = Array(season.values)

  • 添加、修改kv对
    可以使用updateValue方法,如果key存在,将执行更改;如果key不存在,将新增该kv对,并返回nil
  • 删除kv对
    removeValueForKey(key:KeyType)->ValueType?

removeAll(keeyCapacity: Bool = false)
除此之外,还允许将某个key对应的value赋值为nil来删除该kv对

集合的复制
-

  • swift的集合都是结构体类型,即都是值类型的,说白了就是在复制的时候,复制的是副本而不是指针
  • 具体的复制语法用赋值语法就可以了

函数和闭包
=
定义
-
func 函数名(形参列表) [-> 返回值类型]
{

...

}

返回值为元组
-

例如:
func divide(num:Double) -> (String, String)
result = func(10.01)
println(result.0)

func divide(num:Double) -> (max:Int, min:Int)
result = func(10.01)
println(result.max)

函数的形参
-

局部形参名 : 形参类型
func girth(width:Int, height:Int) -> Double
调用:girth(12, 20.1)

外部形参名 局部形参名 : 形参类型
func girth(kuan width:Int, gao height:Double) -> Double
调用:girth(kuan:12, gao:20.1)

如果想让外部形参名与局部形参名一致,则只需要在局部形参名前面加上#号即可
func girth(#width:Int, height:Double) -> Double
调用:girth(width:12, 20.1)

形参默认值
-
[外部形参名] 局部形参名 : 形参类型 = 默认值
程序会自动为设定了默认值的的形参设定一个相同名称的外部形参名,如果不想这么做,可以在局部形参名前加上一个下划线即可取消。

个数可变的形参
-
在类型后面添加...即可

例如:
func test(a:Int, books:String...)

可以用for-in遍历books形参
注意:个数可变的形参只能位于参数列表的最后

常量/变量形参
-
默认情况下,程序会自动将参数列表中的参数使用let声明,如果需要的话,可以手动声明为var

in-out形参
-
一般的形参都为值类型,即将变量的“副本”传入函数,如果需要将地址传入,则需要在参数前面加上inout关键字,个人感觉很像C语言里的传地址。

例如:
func swap(inout a:Int, inout b:Int)
调用:swap(&a, &b)

使用函数类型作为形参类型
-

例如:
func map(var #data:[Int], #fn:(Int)->Int) -> [Int]

使用函数类型作为返回值类型
-

例如:
func getMathFunc(#type:String) -> (Int) -> Int

闭包表达式
-

{ (形参列表) -> 返回值类型 in
    // 零条到多条可执行语句
}

调用闭包
-

var square = { ... }
println(square(5))

省略形参名
-
在省略形参名和in关键字的情况下,可以使用$0 $1的名字来引用第一个、第二个形参……

尾随闭包
-
当第二个参数是函数类型的时候,可以将闭包放在圆括号后面

例如:
someFunction(20){ ... }

闭包是引用类型
-


面向对象
=
枚举
-

  • 定义
enum 枚举名
{
    // 枚举值
    // 其他成员
}

例如:
enum season
{
    case Spring
    case Summer
    case Fall
    case Winter
}
enum Weekday
{
    case Monday, Tuesday, ..., Sunday
}
  • 声明变量
    var day : Weekday
    day = Weekday.Sunday

    day = .Sunday // 当程序可以推断出枚举类型时,可以省略枚举名前缀

  • 原始值
enum 枚举名 : 原始值类型
{
    case 枚举值 = 原始值
}

如果原始值是Int,则程序自动从0开始编号,如中间遇到特定值,则后面的元素依次递增
如果非Int,则必须为每个都指定原始值

可以使用rawValue获取枚举值的原始值
可以使用init?(rawValue)根据原始值返回对应的枚举值

  • 关联值
    关联值类似于枚举值的属性,语法:case 枚举值(元组语法)
例如:
enum Planet
{
    case Mercury(weight: Double, name: String)
    case Earth(Double, Int)
}
var p1 = Planet.Earth(1.0,500)

switch(p1)
{
    case Planet.Earth(var weight, var name):
        ...
    default:
        break 
}

switch(p1)
{
    case let .Earth(weight:w, name:n):
        println("weight:\(w), name:\(n)")
    default:
        break 
}

结构体和类
-

  • 定义
[修饰符] class 类名
{
    构造器
    属性
    方法
    下标
}

* 修饰符可以是 private | internal | public 、final

[修饰符] struct 结构体名
{
    构造器
    属性
    方法
    下标
}
* 修饰符可以是 private | internal | public
  • 定义存储属性
    [修饰符] var|let 存储属性名 : 类型 [= 初始值]

修饰符可以是:private | internal | public 、static | class 、final
类中的属性可以用class修饰,结构体、枚举中的属性可用static修饰
只有类才可以用final

  • 定义方法
    [修饰符] func 方法名(形参列表) [ -> 返回值类型] { ... }

修饰符可以是:private | internal | public 、static | class 、final
类中的属性可以用class修饰,结构体、枚举中的属性可用static修饰
只有类才可以用final

  • 构造器
[修饰符] init(形参列表)
{
    ...
}
* 修饰符可以是public | internal | private
  • 创建实例
var p:Person
p = Person()
或者
var p = Person()
  • 引用类型与值类型的比较
    ===比较的是是否指向同一对象

===!==不能比较值类型的变量

存储属性
-

  • 延迟存储属性
    lazy var myVar = ...

延迟存储属性只有在第一次被调用的时候才会被计算初始值

  • 计算属性
[修饰符] var 计算属性名 : 属性类型
{
    get
    {
        ...
    }
    [set(形参名)
     {
        ...
     }]
}
* 计算属性的setter方法总是只能带一个形参,且该形参的类型与该计算属性的类型相同,因此可以省略掉setter的形参名,使用系统自带的隐式形参名newValue
* 只读计算属性:只有get部分,没有set部分的计算属性,只能返回一个值,定义的时候可以省略掉get关键字和花括号
  • 属性观察者
    willSet(newValue)即将被赋值之前自动调用该方法

didSet(oldValue)被赋值完成后自动调用该方法
可以省略掉形参部分

方法
-

  • 将方法转换为函数
例如:
var f1 : ()->() = sc.test 
  • 方法的外部形参名
    系统默认为方法除第一个形参之外的其他形参都添加了外部形参名,与局部形参名相同;如果希望与局部形参名不同,也可以自己指定或取消外部形参名,与函数类似
  • 值类型的可变方法(适用于结构体与枚举)
    需要修改内部存储属性的方法要在前方加上mutating

下标
-

  • 基本用法
subscript(形参列表) -> 返回值类型
{
    get
    {
        ...
    }
    [set(形参名)
     {
        ...
     }]

}
* 大部分用法与计算属性类似
  • 下标重载
例如:
extension String
{
    subscript(start:Int, end:Int) -> String
    {
        ...
    }
}

类属性与类方法
-

  • 值类型的类属性与类方法
    使用static修饰
  • 类的类属性与类方法
    使用class修饰

构造器
-

  • 构造器的外部形参名(与类方法相似)
    第一个形参的外部形参名与局部形参名相同,第二个使用显式指定的外部形参名
  • 可能失败的构造器
    init?(){...}

继承
-

  • 基本语法
修饰符 class SubClass : SuperClass
{
    ...
}
* 每个类最多只有一个直接父类
  • 重写方法、属性、属性观察者、下标
    需要在方法前面用关键字override注明
  • 防止重写
    使用final关键字注明

构造与析构
-

  • 析构器
    deinit
  • 便利构造器
    便利构造器要调用指定构造器
例如:
convenient init(形参)
{
    ...
}
  • 构造器链

    • 子类构造器必须调用直接父类的指定构造器
    • 便利构造器必须调用同一个类中的其他构造器
    • 便利构造器调用的构造器链最终结点必须是指定构造器
  • 子类必须包含的构造器
    使用required关键字修饰,通过继承或者自行编码均可

多态
-
引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行其运行时类型所具有的方法

is运算符检查类型
-
判断前一个引用变量是否引用后一个类,或者其子类的实例

as运算符向下转型
-
向下转型只能在具有继承关系的两个类型之间进行,否则会出现编译错误

Any和AnyObject
-
AnyObject可以代表任意类的实例
Any可代表任何类型

嵌套类型
-

  • 可以在一个类型的内部定义另外一个类型,支持在枚举、结构体、类的内部嵌套枚举、结构体、类,支持多级嵌套
  • 嵌套类型不允许使用static或者class修饰

拓展
-

[修饰符] extension 已有类型 : 协议1, 协议2, ...
{
    ...
}

协议
-

  • 定义协议
[修饰符] protocol 协议名 : 父协议1, 父协议2, ...
{
    ...
}
  • 实现协议
struct 结构体名 : 协议1, 协议2, ...
{
    ...
}

class 类名 : 父类, 协议1, 协议2, ...
{
    ...
}
  • 协议指定的属性要求
[class] var 属性名 : 类型 { get [set] }
* class:可有可无,如果用class修饰,证明是类属性,不可用static代替class
* 只需写get set即可,不用提供实现
  • 合成协议
    protocol<协议1, 协议2, 协议3, ...>
  • 唯类协议
    这种协议只能被类实现,不能被枚举、结构体实现
定义:
protocol 协议名 : class, 协议1, 协议2, ...
{
    ...
}
  • 可选协议
    添加optional关键字,还要添加@objc修饰
例如:
@objc protocol MyProtocol
{
    optional var status : String { get }
}
  • 输出实例和Printable协议
    需要实现description名称的只读计算属性,然后就可以通过println(instanceName)打印出“自我描述”信息了
  • 使用自定义类型作为字典的key
    需要满足两个条件:

    • 两个key通过==比较返回true(实现Equatable协议,重载==运算符)
    • 两个key的hashValue属性返回相等的整数(实现Hashable协议)

隐藏与封装
-
所有实体只能指定比它所依赖的实体更低或相等的访问权限

  • public可以被任意源文件或任意模块使用
  • internal可以被同一个模块或应用的其他实体使用
  • private只能在当前源文件中使用

使用访问权限定义类型:

  1. 元组的访问权限等于组成该元素的所有成员类型的最低级别
  2. 函数的访问权限默认为internal
  3. 枚举中所有成员的访问权限与该枚举的访问权限相同
  4. 子类访问权限不得高于父类的访问权限
  5. 协议成员与协议总是具有相同的访问权限

内存管理
-

  • 弱引用
    弱引用不会增加对方的引用计数,在定义属性的var关键字前面使用weak关键字即可定义弱引用,弱引用可以接受nil
  • 无主引用
    与弱引用类似,使用unowned关键字定义,但是不能接受nil

泛型
=
定义
-

func 函数名 <T, S> (形参列表) -> 返回值类型
{
    ...
}

例如:

func copyArray <T> (src:[T], inout dest:[T])
{
    ...
}

func projection <SrcType, DescType> (src:[SrcType] ,fn:(SrcType) -> DescType) -> [DescType]
{
    ...
}

泛型类型
-

例如:
struct FkRect<T>
{
    var x : T
    var y : T
    var position: (T, T)
    {
        return (self.x, self.y)
    }
}

let rect = FkRect<Double>(x:1.2, y:2.1)

class Apple<T>
{
    var info : T
    init(info: T)
    {
        self.info = info
    }
}

var a1 = Apple<String>(info:"apple")

从泛型派生子类
-
public class A<E> : Apple<String>{ }

关联类型
-

例如:
protocol Container
{
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

struct IntList: Container
{
    typealias ItemType = Int // 可以省略
    var items = [Int]()
    mutating func append(item: Int)
    {
        items.append(item)
    }
    ...
}

struct List<E>: Container
{
    var items = [E]()
    mutating func append(item:E)
    {
        items.append(item)
    }
    ...
}

var strList = List<String>()

where子句
-
<类型形参: 父类型, 类型形参2: 协议, ... where 条件>


运算符重载
=
前置和后置运算符
-
prefix func -- <T> (inout array:[T]) -> [T]{ }
postfix func -- <T> (inout array:[T]) -> [T]{ }

自定义运算符
-
prefix|infix|postfix operator 运算符名 { }

例如:
infix operator ** { }
func ** (base: Int, exponent: Int) -> Int { ... }

OC与swift混编
=
swift调用oc
-
在swift项目中第一次添加oc类的时候,会提示添加桥接头文件
使用时,在桥接头文件中加入相应的“.h”头文件
构造器关系:
OC: [[类名 alloc]initWithXxx:参数1 yyy:参数2 zzz:参数3]
Swift: 类名(xxx:参数1, yyy:参数2, zzz:参数3)

oc调用swift
-
需要在swift文件中 class关键字前面加上@objc
调用关系同上

标签: none