Swift学习之特殊知识点

版权声明:不积跬步无以至千里,不积小流无以成江海! https://blog.csdn.net/Bolted_snail/article/details/86677916

1.print(str)

  • print函数能够将表达式的结果输出到控制台,类似C的printf函数和OC的NSLog函数。
  • print函数定义:
public func print(_ items: Any..., //任意个数,任何类型的参数它们将输出到控制台
 separator: String = default,//输出多个参数之间的分隔符
  terminator: String = default)//输出字符串之后的结束符号

参数separatorterminator都可以省略,separator只有字输出数据大于1时才有意义。

  • 示例:
print("a","b","c","d","e", separator: "|", terminator: "-")

打印结果为a|b|c|d|e-,用|将打印的字母分开,用-来结束打印。

  • 自定义打印日志:
//自定义log p28
func hjLog(mes:Any){
    print("file:\(#file) column:\(#column)  line:\(#line) \(mes)")
}

由于Swift中不能定义宏,只能定义一个打印日志的方法,上面方法打印文件路径,行数,及要打印的内容,可以方便的定位到打印的位置。

2.浮点型取余

  • Swift中允许浮点型取余,但是在Swift3之后取余运算符%不能应用于浮点数运算,需要使用public func truncatingRemainder(dividingBy other: Double) -> Double方法来计算浮点型取余。
var c = 19.22
//浮点型取余 
let d = c.truncatingRemainder(dividingBy: 6.1)

3.Swift中的数据类型

  • 根据这些类型在赋值或给函数传递时的方式,可分为值类型引用类型值类型就是创建一个副本,把副本赋值或传递过去,这样在函数的调用过程中不会影响原始数据;引用类型就是把数据本身的引用(即指针)赋值或传递过去在函数的调用过程中会影响原始数据。
  • Swift中整型、浮点型、布尔型、字符型、字符串、元组、集合、枚举。结构体值类型,而类属于引用类型。如Swift中String是值类型、NSString是引用类型。
  • 整型、浮点型、布尔型、字符型、字符串、元组、集合等类型本质上都是结构体类型,结构体有构造函数,通过构造函数创建并初始化实例。
  • 用于判断的几种方式:
    1. ==比较基本类型的值是否相等
    2. ===比较对象类型,是不是同一个对象
    3. is判断某个实例是否为某种类型
    4. as强制类型转换

4.Swift中的for循环

  • Swift3之后C语言风格的for语句不再使用, Swift3之后for语句只能与in关键字结合使用。
  • 示例1:只需要知道连续区间的值(只需要循环变量)
//打印1到9平方的值
for i in 1..<10{
    print("\(i)x\(i)=\(i*i)")
}
  • 示例2:只需要连续获取集合元素的值(不需要循环变量)
//打印集合元素
let nums = [1,2,3,4,5,6,7,8,9]
for item in nums {
    print(item)
}
  • 示例3:既需要集合元素的值,又需要脚标(需要循环变量),可以使用结合的enumerated()方法
let nums = [1,2,3,4,5,6,7,8,9]
for(index,item) in nums.enumerated(){
    print("\(index):\(item)")
}
  • Swift中的区间分为两种,全闭合区间...和左闭右开区间..<

5. break语句

  • break语句可用于循环语句结构,(Swift中的循环语句有while、repeat-while、for ),作用是强制退出循环结构,不执行循环结构中的剩余语句。由于Swift的选择switch默认会添加break,所以一般不需要收到添加break,但添加上也不会出问题。
for i in 1...10{
    print("\(i)x\(i)=\(i*i)")
    if i == 5{
        break;
    }
}
  • break可以配合标签使用
label1: for x in 0..<5 {
    label2: for y in (1...5).reversed(){
        if x == y {
            break label1
        }
        print("(x,y)=(\(x),\(y))")
    }
}

默认情况下break只会跳出最近的内循环,而示例中条件成立时直接跳出循环,给外循环添加了一个标签label1,然后break后面指定这个标签,当条件成立就跳出该标签的外循环。reversed()是反向变量区间。

6.Set集合

  • Swift集合有数组、字典、Set集合数组是一组有序的由相同类型元素构成的集合;字典由两部分组成,一个键集合,一个值集合,键集合不能有重复元素,而值集合可以重复,键值成对出现;Set集合是由一串无序的,不能重复的相同类型元素构成的集合。
  • 与OC任何对象类型不同,三种集合都强调是元素的类型一致,但这里的类型指的是泛型。如果想包含任何类型可指定为Any
//正规声明
var arr : Array<Any> = [1,2,"3",1.22]
print(arr[1])

//简写声明
let dic:[AnyHashable : Any] = [1:2,2:3,"3":4,4:"5"]
//字典的key和value都为任何类型,有可能是可选类型,获取元素值时要解包
print(dic[1]!)

let set:Set<String> = ["1","12","123"]
print(set.first!)
  • Array和Dictionary有简写的声明,而Set集合没有简写的声明。因为Set和Array唯一的区别是无序且不重复(当不考虑叙述而且没有重复的元素时,二者可互相替换),如果简写了就无法区分是Set还是Array了。
  • 三种集合强调的点不同, Array强调有序;Set强调不重复(无序);字典强调key唯一(不能重复),通过key取值,也是无序的。
  • Set的一般操作:
let set:Set<String> = ["1","123","12","12","1234"]
print("第一个元素\(set.first!)")
print("元素个数\(set.count)")
var set2:Set<String> = ["1234","1","123","12"]
if set == set2{
    print("set等于set2")
}
//插入一个元素
set2.insert("插入")
//删除某个元素
let item = "1"
set2.remove(item)
print(set2)
//删除一个元素,这里并不是第一个元素,而是随机的
set2.removeFirst()
print(set2)
//判断是否包含某个元素
if set.contains("1234"){
    print("set有该元素")
}

Set一般操作
结果可以看出Set的first方法获取的并不一定是第一个元素,而是随机的。多个重复的元素在Set中只算一个,从count可以看出。

  • Set集合遍历
for item in set{
    print(item)
}

for (index,item) in set.enumerated(){
    print("\(index+1):\(item)")
}

Set遍历
注意:Set的enumerated()方法可以取出Set的索引和元素, (index,item)是元组类型。这里的index是循环遍量,可以表示循环次数,而不是元素的序号脚标。

  • Set集合间的运算,首先了解一下几个概念(A,B是两个Set集合):
  1. 交集:属于A且属于B的元素集合
  2. 并集:属于A或属于B的元素集合
  3. 异或集合:A与B的并集元素集合去掉A与B的交集集合中的元素后剩下元素的集合
  4. 差集:属于A而不属于B的元素集合称A与B的差集。(A与B的并集去掉B中所有元素后的集合)
  5. 子集:B中所有元素都A的元素,那么久称B是A的子集。但是Set集合运算过程中不涉及Set子集概念。
let A:Set<String> = ["a","b","c","d"]
let B:Set<String> = ["c","d","e","f"]
print("A与B的交集 = \(A.intersection(B))")
print("A与B的并集 = \(A.union(B))")
print("A与B异或集合 = \(A.symmetricDifference(B))")
let C = A.subtracting(B)
print("A与B差集 = \(C)")
if C.isSubset(of: A){
   print("C是A的子集")
}

Set集合间的操作

7.函数

7.1 .Swift 函数参数
  • Swift中的函数参数很灵活,具体体现在传递参数有多种形式。
  • 我们可以为每个参数提供标签,为调用者说明参数的含义,这些变迁命名应该唯一,并且有意义:
   func rectangleArea(W width:Double,H height:Double) ->Double{
   return width*height
}
print(rectangleArea(W: 10.0, H: 10.0))

W,H就是参数标签,外部调用时会提示使用。

  • 如果定义函数没有声明标签,原则上也是可以的
func rectangleArea2(width:Double,height:Double) ->Double{
  return width*height
}

print(rectangleArea2(width: 10.0, height: 10.0))
  • 省略参数标签,在Swift3以后,调用函数时要求指定所有参数的标签,除非函数定义是使用下划线_关键字声明的标签。
func rectangleArea3(_ width:Double,_ height:Double) ->Double{
  return width*height
}
print(rectangleArea3(10,10))
  • 给参数设默认值,当调用时可以忽略该参数,调用时如果没传值就取默认值,如果赋值了就会覆盖掉默认值
func test(a:Int = 10,b:Int) -> Int{
  return a+b
}
print(test(b: 20)) //30
print(test(a: 1, b: 2)) //3
  • 可变参数:参数个数可以变化,调用时可以接受不确定数量的参数,这些参数具有相同的类型,有点像传入了一个数组
func sum(nums:Int...)->Int{
  var total = 0
  for num in nums{
   total += num
  }
  return total
}
print(sum(nums: 1,2,3))
print(sum(nums: 1,2,3,4,5,6,8))

sum函数是用来求多个整型的函数,参数nums:Int…是Int类型的可变参数,在函数体重nums被认为是一个Double类型的数组

  • 类型参数的引用传递:除了类是引用类型,其他如整型…都是值类型,但有时候我们想在函数内部改变函数外面参数的值,这样就需要将值类型参数以引用类型方式传递。
func increment(value: inout Double,auto:Double = 1.0){
   value += auto
}
var value:Double = 10.0
print(value)
increment(value: &value)
print(value) //11.0

参数value是需要增长的数值,它被设计为inout类型,inout修饰的参数称为输入输出参数,value必须是变量不能是let修饰的常量。

7.2. Swift函数返回值
  • 函数返回值分为无返回值和有返回值,无返回值其类型是void,可以省略不写;有返回值又分为单个返回值,和多个返回值,单个返回值就是返回一种类型,多个返回值可以返回多个类型,将这些不同类型的返回值放到元组中返回就可以了。
7.3. Swift函数类型
  • 每个函数都有一个类型,使用函数类型与使用其他数据类型一样,可以声明变量或常量,也可以作为其他函数参数或返回值使用。
  • 用函数类型声明常量或变量
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
let rectangleArea1: (Double,Double)->Double = rectangleArea(width:height:)
print(rectangleArea1(10,10)) //100.0

var rectangleArea2: (Double,Double)->Double = rectangleArea(width:height:)
rectangleArea2(20,20)
rectangleArea2 = rectangleArea(width:height:)
print(rectangleArea2(30,30)) //900.0
  • 用函数类型作为函数返回类型使用
//计算矩形面积
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
//计算三角形面积
func triangleArea(width: Double,height:Double) -> Double {
    return width * height/2.0
}

//作为函数返回类型使用
func getArea(type:String) -> (Double,Double)->Double {
    var returnFunc: (Double,Double)->Double
    switch type {
    case "矩形":
     returnFunc = rectangleArea
    default://三角形
      returnFunc = triangleArea
    }
    return returnFunc
}
let rectangleFunc = getArea(type: "矩形")
print("矩形面积:\(rectangleFunc(20,20))")  //矩形面积:400.0
let triangleFunc = getArea(type: "三角形")
print("三角形面积:\(triangleFunc(20,20))") //三角形面积:200.0

getArea返回值类型为函数类型,常量rectangleFunctriangleFunc只是接收了getArea函数的返回值,是将计算矩形面积和三角形面积的函数真正声明了,rectangleFunc(20,20)和triangleFunc(20,20)才是对计算面积的函数的真正调用。总之就是getArea函数调用是为了声明rectangleAreatriangleArea,返回的函数常量或变量的调用才是真正用于计算面积的,

  • 用函数类型做为函数参数类型使用
//计算矩形面积
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
//计算三角形面积
func triangleArea(width: Double,height:Double) -> Double {
    return width * height/2.0
}
//作为参数类型使用
func getAreabByFunc(funcName:(Double,Double)->Double,a:Double,b:Double)->Double {
    let area = funcName(a,b) //对传进来函数参数的真正调用
    return area;
}
print("矩形面积:\(getAreabByFunc(funcName: rectangleArea, a: 10, b: 10))")  //矩形面积:100.0
print("三角形面积:\(getAreabByFunc(funcName: triangleFunc, a: 10, b: 10))") //三角形面积:50.0

getAreabByFunc调用值只需要将函数名传进来就可以,传进来的函数参数直接在里面调用。
7.4. Swift嵌套函数

  • 将函数定义在另外的函数体重,称为嵌套函数
//嵌套函数
func calculate(opr:String) -> (Int,Int) -> Int {
    //定义加函数
    func add(a:Int,b:Int) -> Int{
        return a + b
    }
    //定义减函数
    func sub(a:Int,b:Int) -> Int{
        return a - b
    }
    var result: (Int,Int) -> Int
    switch opr {
    case "+":
        result = add
    case "-":
        result = sub
    default:
        result = add
    }
    return result
}
let addfunc = calculate(opr: "+") // 声明加函数
print("5+5 = \(addfunc(5,5))") //5+5 = 10
let subfunc = calculate(opr: "-")// 声明减函数
print("5-5 = \(subfunc(5,5))")//5-5 = 0

嵌套函数的作用域在外函数体内,但我们可以定义外函数的返回值类型为嵌套函数类型,从而将嵌套函数出啊递给外函数,被其调用者调用。

8.运算符重载

  • Swift中除了class类型是引用类型,其他整型,浮点型,数组,结构体等都是值类型,对于引用类型通常用===!===来判断是不是同一个对象;对于值类型通常用==!=来判断两个值是否相等。
  • 示例:自定义一个机构体,判断两个实例是否相等。
struct Student {
    var name = ""
    var no = 0
    var age = 0
}

var stu1 = Student()
stu1.name = "张三"
stu1.no = 1
stu1.age = 18

var stu2 = Student()
stu2.name = "张三"
stu2.no = 1
stu2.age = 18

if stu1 == stu2{
    print("是同一个学生")
}else{
    print("不是同一个学生")
}

在这里插入图片描述
可以发现在运行时报错了,报错说明为==不能用于两个Student结构体实例操作。说了stu1和stu2不能用于比较。我们需要在这些类型中重载==!=运算符号。即定义相等规则。

struct Student {
    var name = ""
    var no = 0
    var age = 0
}
//定义重载==号运算符符
func == (lsh:Student,rhs:Student) -> Bool {
    return lsh.name == rhs.name && lsh.no == rhs.no && lsh.age == rhs.age
}
//定义重载!=号运算符符
func != (lsh:Student,rhs:Student) -> Bool {
    return (lsh.name != rhs.name || lsh.no != rhs.no || lsh.age != rhs.age)
}

var stu1 = Student()
stu1.name = "张三"
stu1.no = 1
stu1.age = 18

var stu2 = Student()
stu2.name = "张三"
stu2.no = 1
stu2.age = 18

if stu1 == stu2{
    print("是同一个学生")
}else{
    print("不是同一个学生")
}

在这里插入图片描述

9.类型嵌套

  • Swift中的类、结构体和枚举可以进行嵌套。优点是支持访问它外部的成员(包括方法、属性和其他嵌套类型),嵌套可以有多个层次。
class Employee {
    var name = ""
    var no = 0
    var job = ""
    var day = WeekDays.Friday
    var dept = Department()
    
    struct Department {
        var no = 10
        var name = "Sales"
    }
    enum WeekDays{
        case Monday,Tuesday,Wednesday,Thursday,Friday
        
         struct Day {
            static var mes = "Today is ..."
        }
    }
    
    
}
let emplo = Employee()
print(emplo.day)
print(emplo.dept.name)
print(Employee.WeekDays.Day.mes)

10.类和结构体的异同。

  • 相同点:
    1. 定义存储属性;
    2. 定义方法;
    3. 定义下标;
    4. 定义构造函数;
    5. 定义扩展;
    6. 实现协议
  • 不同点:(只有类才有的功能)
    1. 能够继承另外一个类;
    2. 能够核对运行时对象的类型;
    3. 析构对象释放资源;
    4. 引用计数允许一个实例有多个引用。
  • 选择的原则:结构体是值类型,每一个实例没有独一无二的标识,而类是引用类型,每一个实例都是独一无二的标识。
class Employee{ //员工类
  var no = ""
  var name = ""
  var dept: Department?
  
}
struct Department{//部门结构体
  var no = ""
  var name = ""
}

上面示例可以看出,由于员工的编号都是独一无二,每个员工是独立的个体,所以员工可以声明成类Employee;如果具有相同部门标号和部门名称,我们就认为是它们是相同的部门,所以就可以把部门设计为机构体Department。

11.属性与下标。

  • Swift中的属性分为存储属性计算属性。存储属性就是oc中的数据成员,计算属性不存储数据,但可以通过计算其他属性返回数据。
class Employee{
  let no = 0
  var firstName = "Tony"
  var lastName = "Guan"
  lazy var dept: Department = Department() //延迟存储属性
  var fullName: String{  //计算属性
      get{
       return firstName + "." + lastName
          
      }
      set(newFullName){
          let names = newFullName.components(separatedBy: ".")
          firstName = names.first!
          lastName = names.last!
      }
//        set{
//            let names = newValue.components(separatedBy: ".")
//            firstName = names.first!
//            lastName = names.last!
//        }
  }
  
}
struct Department{
  var no = ""
  var name = ""
}
let emp = Employee()
//emp.no = 10;编译会报错 ,常量属性不允许被修改。
print(emp.fullName)
emp.fullName = "Jack.Ma"
print(emp.fullName)
let dept = Department()

上面类Employee中的no、firstName、lastName、dept都是存储属性,fullName是计算属性,其中dept是延迟存储属性。

  • 延迟存储属性:存储属性前面加上lazy关键字声明,就是延迟存储属性,只在第一次访问时加载,如果不访问就不会创建,这样可以减少内存占用,注意存储属性没有延迟加载一说,添加到存储属性前面会报错的。
  • 计算属性:计算属性本身不存储数据,而是从其他属性计算得到数据。计算属性提供一个Getter(取值访问器)来获取值,以及一个可选的(可以不实现set方法)Settter(设置访问器)来间接设置其他属性或变量的值,语法如下。
面向对象的类型(class、struct、enum) 类型名{
存储属性
var 计算属性名: 属性数据类型{
 get{
 return 计算后的语句值
 }
 set(新属性值){
 语句组
}
}
}

其中新属性值是要赋值给属性值的,当然也可以不写,Swift提供了一个默认的变量newValue去接收新传入的值。上面set(newFullName)可以省略如下:

  set{
           let names = newValue.components(separatedBy: ".")
           firstName = names.first!
           lastName = names.last!
       }
  • 只读计算属性:计算属性只有Getter访问器,没有Setter访问器,只能取值而不能赋值。(只读存储属性就是let修饰的存储属性)
class Employee{
   let no = 0
   var firstName = "Tony"
   var lastName = "Guan"
   lazy var dept: Department = Department()
   var fullName: String{
       get{
        return firstName + "." + lastName
           
       }
   }
}
let emp = Employee()
//emp.no = 10;
print(emp.fullName)
// emp.fullName = "Jack.Ma" //不能赋值

fullName就是只读计算属性,不能给其赋值,结构体,枚举的计算属性也是类似,这里不再赘述。只读属性可以简化去掉get关键字和括号,上面只读属性可以简化为:

   var fullName: String {
        return firstName + "." + lastName   
       }
  • 存储属性观察者:Swift的属性观察者有两个,willSet:观察者在修改之前调用,didSet观察者在修改之后调用 语法格式如下。
面向对象类型(class/struct) 类型名{
   ...
   var 存储属性值: 属性数据类型 = 初始值{
       willSet(新值){
           ...
       }
       didSet(旧值){
           ...
       }
   }
}

示例

class Employee1{
    let no = 0
    var name = "Tony"{
        willSet(newName){
            print("员工新名字:\(newName)")
        }
        didSet(oldName){
            print("员工旧名字:\(oldName)")
        }
    }
}
struct Department1{
    var no = 1{
        willSet{
            print("部门新编号:\(newValue)")
        }
        didSet{
            print("部门旧编号:\(oldValue)")
        }
    }
    var name = "移动开发部"
}

let emp1 = Employee1()
emp1.name = "Jack"
//结构体是值类型,必须用声明为变量才能修改其属性
var dept1 = Department1()
dept1.no = 10

Employee1类中给name存储属性添加了观察者, willSet(newName)中的newName是要传进来的新值,didSet(oldName)中的oldName是新值传进来之前的旧值;参数的声明可以省略。Department1结构体中的no就省略了观察者参数,Swift提供了对应默认参数,新值默认是newValue,旧值默认是oldValue。这里需要说明下枚举只有计算属性没有存储属性,所以枚举不支持属性观察者

  • 静态属性:在属性前面加static关键字,类中也可以加class关键字,这样的属性称为静态属性。类,结构体,枚举都可以定义静态属性(也包括静态存储属性和静态计算属性)。这里需要注意的是枚举中没有实例存储属性但是可以有静态存储属性
struct Account {
    var amount = 0.0 //账户金额
    var ower = "" //账户名
    static let monthRate = 0.00688 //月利率
    static var yearRate : Double{  //计算存储属性不能用let修饰,及不能是常量
        return monthRate * 12
    }
    var owerInterest:Double{ //年利息
        return Account.yearRate * amount
    }
}
//访问静态属性
print(Account.yearRate)
var account = Account()
//访问实例属性
account.amount = 100000.0
print(account.owerInterest)


class Account {
    var amount = 0.0 //账户金额
    var ower = "" //账户名
    static let monthRate = 0.00688 //月利率
    class var yearRate : Double{  //class换成static子类就不能重写该属性
        return monthRate * 12
    }
    var owerInterest:Double{ //年利息
        return Account.yearRate * amount
    }
   static var test:Int = 10{ //静态属性也支持添加属性监听者
        willSet{
            print(newValue)
        }
        didSet{
            print(oldValue)
        }
    }
}
//访问静态属性
print(Account.yearRate)
var account = Account()
//访问实例属性
account.amount = 100000.0
print(account.owerInterest)

class Account2:Account{
    override class var yearRate : Double{  //class换成static子类就不能重写该属性
        return monthRate * 12 * 1.1
    }
}

这里枚举与结构体类似不再举例,类中static修饰的属性为类的静态属性,class修饰的属性称为类的类属性,区别是类的类属性可以被子类重写,但是class不能修饰存储属性,static却可以

  • 实例属性和静态属性总结
    1. 类、结构体,枚举都支持静态存储属性、静态计算属性,实例计算属性;但是只有类和结构体支持实例存储属性,枚举不支持实例存储属性。
    2. 基于第一条可知只有类和结构体支持添加属性观察者(因为只有存储属性才能添加属性观察者)
    3. 延迟属性只能是延迟存储属性
    4. 计算属性必须是var声明的
    5. let修饰的存储属性,前可以加static称为静态存储属性,不能加class
    6. class修饰的属性可以被重写,但static修饰的属性不允许被重写。
    7. class只能修饰计算属性。
  • 下标:Swift中,我们可以定义一些集合类型,,它们可以回有一些集合类型的存储属性,这些属性可通过下标访问,其如法格式如下。
面向对象类型(class/struct/enum) 类型名{
   ...
   subscript (参数:参数数据类型)->返回值类型{
       get{
           return 返回值
       }
       set(新属性值){
           ...
       }
   }
}

Swift中没有提供二维数组,但是我们可以通过下标自定义一个二维数组。

struct DoubleDimensionalArray {
   let rows:Int,colums:Int
   var grid: [Int]
   init(rows:Int,colums:Int) {
       self.rows = rows //行数
       self.colums = colums //列数
       grid = Array(repeating: 0, count: rows * colums) //初始化数组都为0
   }
   subscript(row:Int,col:Int) -> Int{
       get{
           return grid[row * colums + col] //返回对应的脚标取出二维数组的值
       }
       set{
           grid[row * colums + col] = newValue //通过脚标给二维数组赋值
       }
   }
}
//初始化一个10行10列的二维数组
var arr = DoubleDimensionalArray(rows: 10, colums: 10)
for i in 0..<10 {
   for j in 0..<10{
       arr[i,j] = i*j //通过脚标给二维数组赋值为脚标之和
   }
}

for i in 0..<10 {
   for j in 0..<10{
       print("\t \(arr[i,j])",terminator: " ") //通过脚标获取二维数组中的值
   }
   print("\n")
}

Swift自定义二维数组

11.方法。

  • Swift中,方法是在类,结构体,枚举中定义的函数,分为实例方法和静态方法.
  • 方法和函数的区别:方法是在在类,结构体,枚举内部定义的.方法调用前面要有主体,而函数就不需要.
  • 我们在枚举和结构体方法掐面添加关键字mutatting,将方法声明为可以变方法,可变方法能够修改值类型变量属性,但不能修改值类型常量属性.也就说不可变方法值类型属性是都不能访问的,但引用类型的属性是可以访问的。
  • static修饰的方法为静态方法,当然类中class修饰的方法类方法.与计算属性类似,实例方法中既可以访问实例属性和方法又可以访问静态属性和方法,但是静态方法不能访问实例属性和实例方法,只能访问静态属性和方法.
  • class修饰的方法能被重写,static修饰的方法不能被重写.

12.重写

  • 一个类继承另一个类的属性,方法,下标等特征后,子类可以重写(override)这些特征。
  • 重写实例属性:实例属性重写一方面可以Getter和Setter访问器,另一方面可以重写属性观察者。
class Person {
    var name: String
    var age: Int
    init(name:String,age:Int) {
        self.name = name
        self.age = age
    }
}
class Student: Person {
    var school: String
    override var age:Int {
        get{
            return super.age
        }
        set{
            super.age = newValue<8?8:newValue
        }
    }
}

从属性重写来看,子类本身并不存储数据,数据存储在父类的存储属性中,子类将其变成了计算属性并重写。
注意:一个属性重写了Getter和Setter访问器后就不能重写观察者。另外常量属性和只读计算属性也都不能重写属性观察者。

  • 重写静态属性:
class Account{
    class var staticProp:Double{
        return 0.0668 * 1000000
    }
}
class TermAccount: Account {
    override static var staticProp:Double{
        return 0.0700*1000000
    }
}

Account的静态属性staticProp只能用class修饰,因为要在子类TermAccount重写该静态属性,所以该属性能被继承才行,而TermAccount可以用class也可以用static修饰,因为没有子类继承TermAccount的staticProp属性。

  • 重写实例静态方法:
class Person {
    var name: String
    var age: Int
    init(name:String,age:Int) {
        self.name = name
        self.age = age
    }
    func description() -> String {
        return "\(name)的年龄为:\(age)"
    }
}

class Student: Person {
    var school: String
    override var age:Int {
        get{
            return super.age
        }
        set{
            super.age = newValue
        }
    }
    override init(name:String,age:Int) {
        self.school = "清华大学"
        super.init(name: name, age: age)
        
    }
    override func description() -> String {
        return "\(name)的年龄为:\(age),所在学校为\(school)"
    }
}

静态方法重写与实例方法重写类似,在方法名钱加上override关键字即可,但是只有class修饰的静态方法才能被被继承被重写,static修饰的静态方法不能被重写。

  • 下标重写:对下标重写也是重写Getter和Setter访问器,类似于属性,这里不再举例。
  • final关键字:final关键字可以声明类、属性、方法、和下标。final关键字可以声明类不能被继承,声明的属性、方法和下标不能被重写。

13.类检查与转换(is、as、as!、as?、AnyObject、Any)

  • 使用is进行类型检查:is操作符可以判断一个实例是否是某个类的类型。(只要是该类型以及该类型的父类以及父类的父类类型,返回都为true,类似于oc的isKindOfClass方法)
//Student继承于Person
let  stu =  Student(name: "马云", age: 18)
if stu is Student{
    print("马云18岁是一个学生")
}
if stu is Person{
    print("马云18岁是一个人")
}

在这里插入图片描述

  • 使用as、as!、as?进行类型转换:
    1. as操作符:用于向上转型(子类转换成父类),因为向上转型很少进行,所以代码中很少能够看到使用as操作符(通常都是向下转型:父类转出子类)。
    2. as!操作符: 在类型转换过程中对可选值进行拆包,转换结果是费可选类型。将费可选类型转换为非可选类型,将可选类型转换为非可选类型。
    3. as?操作符: 在类型转换过程中不进行裁包,转换结果是可选类型。将非可选类型转换为可选类型,将可选类型转换为可选类型。
//Student,Worker类都继承于People
let stu1 = Student(name: "李彦宏", age: 35, school: "北京大学")
let stu2 = Student(name: "马化腾", age: 45, school: "深圳大学")
let stu3 = Student(name: "马云", age: 55, school: "杭州师范大学")
let wk1  = Worker(name: "李开复", age: 56, factory: "微软")
let wk2  = Worker(name: "张小龙", age: 46, factory: "腾讯")
let people = [stu1,stu2,stu3,wk1,wk2]
for p in people{
    if let stu = p as? Student{ //这里在是先转换为可选类型,并且在转换成功后进行了可选绑定(因为都有值就解包了)
        print("Student \(stu.name),年龄:\(stu.age),毕业于:\(stu.school)")
    }else if let wk = p as? Worker{
        print("Worker \(wk.name),年龄:\(wk.age),工作于:\(wk.factory)")
    }
}
let stu4 = people[0]as? Student //这里是直接赋值为可选类型
print("--------")
print(stu4 as Any)
print(stu4?.name as Any) //可选类型安全访问其属性的写法
print(stu4!.name)

在这里插入图片描述

  • 使用AnyObjectAny类型:Swift提供了两种类型来表示不确定类型(任意类型),AnyObject表示任何类(class)的类型;Any则表示任何类型,包括Int、Double、Array、struct等基本数据类型,也包括AnyObject类型。也就是说AnyObjectAny的子集。
  • 在OC与Swift混合编程时,OC的id类型和Swift的AnyObject类型可以互换,但是两者有本质区别。id类型是泛性,可以代表任何指针类型,编译时编译器不检查id类型,是动态的。AnyObject是实实在在表示类的类型,编译时会检查AnyObject类型。
  • 原则上若能使用集体的数据类型,则尽量不要使用AnyObject类型,更要少考虑使用Any类型。从集合中取出这些实例时也要尽可能的将AnyObject类型和Any类型转换为特定类型,然后再进行接下来的操作。

14.扩展(extension)

  • Swift中可以使用一种扩展机制,在原始类型(类、机构体、枚举)的基础上添加新功能。扩展是一种轻量级的继承机制。
  • Swift中扩展机制可以在原始类型中添加新功能包括:
    1. 实例计算属性和静态计算属性
    2. 实例方法和静态方法
    3. 构造函数(结构体中可以扩展构造函数;类中只能扩展便利构造函数,不能扩展指定构造函数和析构函数,指定构造函数和析构函数只能在原始类中提供)
    4. 下标

猜你喜欢

转载自blog.csdn.net/Bolted_snail/article/details/86677916
今日推荐