Swift 基本语法2

一、?和!

1、可选类型: ?

在swift中,可选类型(?)其根源是一个枚举型,里面有None和Some两种类型。其实所谓的nil就是Optional.None, 非nil就是Optional.Some.

! 代表强制解包

 1 // MARK: - ?和!
 2 // ?代表可选类型,实际上的值是枚举类型 -- None和Some,其实nil值相当于Optional.None,非nil就是Optional.Some
 3 // !代表强制解包
 4 
 5 // 定义一个Int类型的可选类型变量
 6 var num : Int? = 8
 7 // 把这个类型类型的变量赋值给另一个可选类型的变量
 8 var num1 = num
 9 print(num1)
10 // 打印出来以后是一个Optional类型的值,如果要取到8,必须对可选类型强制解包!
11 var num2 = num!
12 print(num2)
13 
14 var number : Int?
15 // 如果对没值(nil)的变量进行强制解包,会造成程序崩溃
16 //var number1 = number!

 2、可选绑定

可选类型分为有值和没值,如果可选类型的变量没值时对其强制解包,程序就会崩溃 。所以,强制解包是非常危险的

1 var number : Int?
2 // 如果对没值(nil)的变量进行强制解包,会造成程序崩溃
3 //var number1 = number!
4 
5 // 可选绑定
6 // 如果不确定可选类型变量是否有值时,用可选绑定,不需要对可选类型强制解包
7 if var number2 = number {
8     print(number2)
9 }

 3、隐式解析可选类型

隐式解析可选类型和可选类型一样,都是有值和没值(nil)两种结果,区别是赋值时,隐式解析可选类型不需要强制解包。

 1 // 隐式解析可选类型:有值,没值(nil)
 2 
 3 var intNum : Int! = 3
 4 var intNum1 = intNum
 5 print(intNum)
 6 print(intNum1)
 7 
 8 // 如果隐式解析可选类型的变量没值,程序一样会崩溃
 9 
10 var intNumber :Int!
11 //var intNumber1 = intNumber
12 //print(intNumber1)
13 
14 // 可选绑定
15 if var intNumber2 = intNumber {
16     print(intNumber2)
17 }

二、结构体

Swift的结构体对比OC来说,可以添加初始化方法、可以遵守代理协议等

声明一个结构体的格式:
struct 结构体的名字 {
   声明成员变量等

计算属性与存储属性

· 存储属性就是类或结构体里定义的变量(或常量)。存储属性可以是变量存储属性(用关键字 var定义),也可以是常量存储属性(用关键字let定义)。
· 除存储属性外,类、结构体和枚举可以定义计算属性。计算属性不直接存储值,而是提供一个必写的getter和一个可选的setter来间接获取和设置其他属性或变量的值。不可以在计算属性中使用self.否则会造成死循环。

 1 // MARK: - 结构体
 2 // 声明一个结构体
 3 struct Rect {
 4     // 声明结构体变量的属性(存储属性)
 5     // 注意,结构体变量中的属性类型可以使用let去修饰,只不过访问的时候不能对其值进行修改
 6     var x : Float
 7     var y : Float
 8     let width : Float
 9     var height : Float
10     // 声明结构体属性,要使用static
11     static var description : String?
12     // 声明一个计算属性(是用来专门计算结构体变量属性的setter方法和getter方法,其本身并没有存储功能)
13     var centerX : Float {
14         // setter方法(可以不写setter)
15         set {
16             x = newValue   // newValue是外界调用方法时传进来的参数
17         }
18         // getter方法(必须要写getter)
19         get {
20             return x / 2
21         }
22     }
23     
24     var centerY :Float {
25         get {
26             return y / 2
27         }
28     }
29     
30     // 声明方法
31     // 声明结构体变量方法/成员方法(相当于OC中的实例方法)
32     func frameInfor() {
33         print("x:(x), y:(y), (width), height:(height)")
34     }
35     // 声明结构体方法(相当于OC中的类方法),使用static修饰
36     static func infor() {
37         print("这是结构体方法")
38     }
39 }
40 
41 // 1、根据结构体去定义一个结构体变量,结构体有自带的构造方法
42 var frame = Rect(x: 345, y: 22,  42, height: 64)
43 // 2、访问结构体变量中的属性, 用结构体变量去访问
44 frame.height = 22
45 print(frame.height)
46 
47 // 3、访问结构体属性,用结构体去访问
48 Rect.description = ""
49 print(Rect.description)
50 
51 // 4、访问计算属性
52 frame.centerX = 100  // 调用了setter方法
53 print(frame.centerX)  // 调用了getter方法
54 
55 // 5、调用结构体变量方法
56 frame.frameInfor()
57 
58 // 6、调用结构体方法
59 Rect.infor()

结构体遵守代理协议(经自己验证,协议中声明的方法只能是计算属性,否则不能遵守)

 1 // 7、结构体遵守代理协议
 2 
 3 protocol Named {
 4     var name: String { get }
 5 }
 6 protocol Aged {
 7     var age: Int { get }
 8 }
 9 struct Person: Named, Aged {
10     var name: String
11     var age: Int
12 }

三、类

类是人们构建代码所用的一种通用且灵活的构造体。我们可以使用与结构体完全相同的语法规则来为类定义属性(常量、变量)和添加方法。

我们通过关键字class来定义类,并在一对大括号中定义它们的具体内容:
class ClassName {
  类的内部细节
}

声明类方法的方式有两种

使用 static 和 class 都可以修饰类方法,区别在于:class修饰的类方法可以被子类重写;static修饰的类方法不可以被子类重写,防止父类方法被修改

 1 // MARK: - 类(class)
 2 class Person {
 3     // 属性
 4     var name : String?
 5     var age : Int?
 6     
 7     // 初始化方法
 8     init(name : String, age : Int) {
 9         self.name = name
10         self.age = age
11     }
12     
13     // 声明类属性
14     static var introduce : String?
15     
16     // 声明计算属性(setter,getter, 不能用self. 否则会造成死循环)
17     var value : Int {
18         set {
19             age = newValue  // newValue是外界调用方法时从外界传进来的参数
20         }
21         get {
22             return age!
23         }
24     }
25     
26     // 声明类方法,在类方法中只能使用类属性,不能使用对象属性
27     // 1、用static关键字修饰,虽然是一个类方法,但是该方法在子类中不能重写
28     static func sayHi() {
29         print(introduce! + "hi")
30     }
31     // 2、用class关键字修饰,可以被子类重写
32     class func sayHello() {
33         print(introduce! + "hello")
34     }
35     
36     // 声明实例方法
37     func sayBey() {
38         print("BeyBey");
39     }
40 }
41 
42 // 1、创建对象,调用初始化方法
43 var per : Person = Person(name : "MBBoy", age : 12)
44 print(per.name! + "(per.age!)")
45 
46 // 2、访问类属性
47 Person.introduce = "我是XXX"
48 print(Person.introduce!)
49 
50 // 3、访问计算属性
51 per.value = 20;
52 print(per.value)
53 
54 // 4、访问类方法
55 Person.sayHello()
56 Person.sayHi()
57 
58 // 5、访问实例方法
59 per.sayBey()
60 
61 // 定义一个Person类的子类Student,Swift不支持多继承
62 class Student : Person {
63     // 重写父类中的类方法
64     override class func sayHello() {
65         print("子类Student中重写的类方法sayHello")
66     }
67     
68     // 重写父类中的实例方法
69     override func sayBey() {
70         print("子类Student中重写的实例方法sayBey")
71     }
72 }
73 
74 // 1、初始化Student类的对象
75 var student : Student = Student(name: "yyp", age: 11)
76 Student.sayHi()
77 
78 // 2、调用重写的父类中的类方法
79 Student.sayHello()
80 
81 // 3、调用重写的父类中的实例方法
82 student.sayBey()

 值类型与引用类型

  • 值类型:

    值类型(Value Types):每个实例都保留了一分独有的数据拷贝,一般以结构体 (struct)、枚举(enum) 或者元组(tuple)的形式出现。

  • 引用类型:

    引用类型(Reference Type):每个实例共享同一份数据来源(在native层面说的话,就是该类型的每个实例都指向内存中的同一个地址),一般以类(class)的形式出现。

  • 值类型与引用类型使用情形
    • 使用值类型的情形:
              使用==运算符比较实例数据的时候。
              你想单独复制一份实例数据的时候。
              当在多线程环境下操作数据的时候。
    • 使用引用类型(比如class)的情形:
              当使用===运算符判断两个对象是否引用同一个对象实例的时候。
              当上下文需要创建一个共享的、可变的对象时。
  • 值类型与引用类型的区别:
    值类型和引用类型最基本的分别在复制之后的结果。
    • 当一个值类型被复制的时候,相当于创造了一个完全独立的实例,这个实例保有属于自己的独有数据,数据不会受到其他实例的数据变化影响。
    • 复制一个引用类型的时候,实际上是默默地创造了一个共享的实例分身,两者是共用一套数据。因此修改其中任何一个实例的数据,也会影响到另外那个。
 1 // MARK: - 值类型和引用值类型
 2 // 值类型
 3 struct animal {
 4     var name : String?
 5     var age : Int?
 6     
 7     init(name : String, age : Int) {
 8         self.name = name
 9         self.age = age
10     }
11 }
12 
13 var dog : animal = animal(name: "阿福", age: 3)
14 // 将dog变量的值赋给dog1(其实是一个拷贝的过程)
15 var dog1 = dog
16 dog.name = "aFu"
17 print(dog.name)    // 打印结果:aFu
18 print(dog1.name)   // 打印结果:阿福
19 
20 // 引用值类型
21 class pet {
22     var name : String?
23     var age : Int?
24     
25     init(name : String, age : Int) {
26         self.name = name
27         self.age = age
28     }
29 }
30 
31 var cat : pet = pet(name: "", age: 2)
32 // 将cat变量的值赋给cat1(其实是一个引用的过程)
33 var cat1 = cat
34 cat.name = ""
35 print(cat.name)   // 打印结果:萌
36 print(cat1.name)  // 打印结果:萌

 四、协议(protocol)

·  协议定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性
·  类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能
·  任意能够满足协议要求的类型被称为遵循(conform)这个协议

声明一个带有可选函数的协议时,必须用@objc来修饰,可选函数要用optional关键字修饰

类遵守协议时,直接在本类后面的冒号的后面,使用","号分隔;或者写在父类名后面,使用","号分隔

必须实现的协议方法,就必须实现,否则会报错

直接实现可选协议里的方法,会报警告,可以添加 @objc 或者继承NSObjec

 1 // MARK: - 协议(protocol)
 2 // 在Swift中声明协议的时候,协议里有可选方法时,需要使用@objc关键字修饰
 3 
 4 // 结婚协议
 5 @objc protocol MarryProtocol {
 6     func cook()  // 做饭
 7     func wash()  // 洗衣服
 8     optional func hitDouDou()   // 打豆豆,可选方法
 9 }
10 
11 // 离婚协议
12 protocol DivorceProtocol {
13     func DivideMoney()   // 分财产
14 }
15 
16 // 继承与Person,实现了MarryProtocol协议和DivorceProtocol协议
17 // 如果该类是另一个类的子类,先写父类,再写遵循的协议
18 class Man : Person, MarryProtocol, DivorceProtocol {
19     @objc func cook() {
20         print("还好去新东方学了几年的厨艺,终于可以大展身手")
21     }
22     @objc func wash() {
23         print("还好买了台洗衣机")
24     }
25     
26     func DivideMoney() {
27         print("分财产")
28     }
29 }
30 
31 // 创建一个男人
32 var man = Man(name: "韦小宝", age: 22)
33 man.cook()
34 man.wash()
35 man.DivideMoney()

五、扩展(Extension)

·  extension + 类名(结构体名字)可以对一个类和结构体扩展方法

·  extension可以多次对一个类进行扩展,也可以给一个类扩展协议方法

·  扩展只能增加方法,不能增加属性

// MARK: - 扩展(Extension)
// 1、扩展协议中的相关方法
extension Man {
    @objc func hitDouDou() {
        print("打豆豆")
    }
}

man.hitDouDou()

// 2、扩展类方法以及对象方法 (类似于OC的类目(Category))
extension Man {
    // 对象方法
    func sing() {
        print("唱歌")
    }
    // 类方法
    class func dance() {
        print("growl")
    }
}

man.sing()
Man.dance()

六、闭包

· 闭包是自包含的函数代码块,可以在代码中被传递和使用。 Swift 中的闭包与 C 和 Objective-C 中的代码块(block)以及其他一些编程语言中的匿名函数比较相似。
· 闭包可以捕获和存储其所在上下文中任意常量和变量的引用。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
1.语法形式
{(参数)-> 返回值类型  in
      执行语句
}
注意:闭包的函数体部分由关键字 in 引入。 该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。

 1 // MARK: - 闭包
 2 // 求两个数的最大值
 3 /*
 4     在OC中使用Block实现
 5     int (^block)(int a, int, b) = ^int (int a, int b) {
 6         return a > b ? a : b;
 7     }
 8 */
 9 
10 // 使用闭包
11 var myBlock : ((a : Int, b : Int) -> Int)
12 
13 // 第一种方式
14 myBlock = {
15     (a : Int, b : Int) -> Int in
16     return a > b ? a : b
17 }
18 
19 // 第二种方式
20 myBlock = {
21     (a, b) -> Int in
22     return a > b ? a : b
23 }
24 
25 // 第三种方式
26 myBlock = {
27     a, b in
28     return a > b ? a : b
29 }
30 
31 // 第四种方式
32 myBlock = {
33     a, b in
34     // 不用return关键字
35     a > b ? a : b
36 }
37 
38 // 第五种方式
39 myBlock = {
40     a, b in
41     return a > b ? a : b
42 }
43 
44 let max = myBlock(a: 3, b: 6)
45 print(max)