编程

Swift语法2

2019-04-16  本文已影响0人  勇敢的_心_

目录

继承   构造过程   析构过程  可选链  类型转换  协议   泛型  访问控制   相等与相同  内存管理

继承

继承我们可以理解为一个类获取了另外一个类的方法和属性。
当一个类继承其它类时,继承类叫子类,被继承类叫超类(或父类)
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本,并且可以重写它们。
也可以为类中继承来的属性添加属性观察器。
1.基类
没有继承其它类的类,称之为基类(Base Class)。
以下实例中我们定义了基类 StudDetails ,描述了学生(stname)及其各科成绩的分数(mark1、mark2、mark3):

class StudDetails {
    var stname: String!
    var mark1: Int!
    var mark2: Int!
    var mark3: Int!
    init(stname: String, mark1: Int, mark2: Int, mark3: Int) {
        self.stname = stname
        self.mark1 = mark1
        self.mark2 = mark2
        self.mark3 = mark3
    }
}
let stname = "swift"
let mark1 = 98
let mark2 = 89
let mark3 = 76
let sds = StudDetails(stname:stname, mark1:mark1, mark2:mark2, mark3:mark3);
print(sds.stname);print(sds.mark1);print(sds.mark2);print(sds.mark3)

2.子类
子类指的是在一个已有类的基础上创建一个新的类。
为了指明某个类的超类,将超类名写在子类名的后面,用冒号(:)分隔,语法格式如下

class SomeClass: SomeSuperclass {
    // 类的定义
}

实例
以下实例中我们定义了超类 StudDetails,然后使用子类 Tom 继承它:

class StudDetails
{
    var mark1: Int;
    var mark2: Int;
    
    init(stm1:Int, results stm2:Int)
    {
        mark1 = stm1;
        mark2 = stm2;
    }
    
    func show()
    {
        print("Mark1:\(self.mark1), Mark2:\(self.mark2)")
    }
}

class Tom : StudDetails
{
    init()
    {
        super.init(stm1: 93, results: 89)
    }
}
let tom = Tom()
tom.show()
以上程序执行输出结果为:
Mark1:93, Mark2:89

3.重写(Overriding)
子类可以通过继承来的实例方法,类方法,实例属性,或下标脚本来实现自己的定制功能,我们把这种行为叫重写(overriding)。
我们可以使用 override 关键字来实现重写。

重写    访问方法,属性,下标脚本
方法    super.somemethod()
属性    super.someProperty()
下标脚本      super[someIndex]
class SuperClass {
    func show() {
        print("这是超类 SuperClass")
    }
}
class SubClass: SuperClass  {
    override func show() {
        print("这是子类 SubClass")
    }
}
let superClass = SuperClass()
superClass.show()
let subClass = SubClass()
subClass.show()
以上程序执行输出结果为:
这是超类 SuperClass
这是子类 SubClass
class Circle {
    var radius = 12.5
    var area: String {
        return "矩形半径 \(radius) "
    }
}
// 继承超类 Circle
class Rectangle: Circle {
    var print = 7
    override var area: String {
        return super.area + " ,但现在被重写为 \(print)"
    }
}
let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("Radius \(rect.area)")
// 重写属性观察器
class Square: Rectangle {
    override var radius: Double {
        didSet {
            print = Int(radius/5.0)+1
        }
    }
}
let sq = Square()
sq.radius = 100.0
print("半径: \(sq.area)")
以上程序执行输出结果为:
Radius 矩形半径 25.0  ,但现在被重写为 3
半径: 矩形半径为 100.0  ,但现在被重写为 21

4.防止重写
我们可以使用 final 关键字防止它们被重写。
如果你重写了final方法,属性或下标脚本,在编译时会报错。
你可以通过在关键字class前添加final特性(final class)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。

final class Circle {
    final var radius = 12.5
    var area: String {
        return "矩形半径为 \(radius) "
    }
}

构造过程

构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。
Swift 构造函数使用 init() 方法。
与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
类实例也可以通过定义析构器(deinitializer)在类实例释放之前执行清理内存的工作。

1.存储型属性的初始赋值
类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。
存储属性在构造器中赋值时,它们的值是被直接设置的,不会触发任何属性观测器。
存储属性在构造器中赋值流程:

2.构造器
构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名。

init()
{
    // 实例化后执行的代码
}

实例
以下结构体定义了一个不带参数的构造器 init,并在里面将存储型属性 length 和 breadth 的值初始化为 6 和 12:

struct rectangle {
    var length: Double
    var breadth: Double
    init() {
        length = 6
        breadth = 12
    }
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")
以上程序执行输出结果为:
矩形面积为 72.0
struct rectangle {
    // 设置默认值
    var length = 6
    var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")
struct Rectangle {
    var length: Double
    var breadth: Double
    var area: Double
    init(fromLength length: Double, fromBreadth breadth: Double) {
        self.length = length
        self.breadth = breadth
        area = length * breadth
    }
    init(fromLeng leng: Double, fromBread bread: Double) {
        self.length = leng
        self.breadth = bread
        area = leng * bread
    }
}
let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面积为: \(ar.area)")
let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积为: \(are.area)")
以上程序执行输出结果为:
面积为: 72.0
面积为: 432.0
struct Rectangle {
    var length: Double
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    init(frombre bre: Double) {
        length = bre * 30
    }
    //不提供外部名字
    init(_ area: Double) {
        length = area
    }
}
// 调用不提供外部名字
let rectarea = Rectangle(180.0)
print("面积为: \(rectarea.length)")
以上程序执行输出结果为:
面积为: 180.0
struct Rectangle {
    var length: Double?
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    init(frombre bre: Double) {
        length = bre * 30
    }
    init(_ area: Double) {
        length = area
    }
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
以上程序执行输出结果为:
面积为:Optional(180.0)
struct Rectangle {
    let length: Double?
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    init(frombre bre: Double) {
        length = bre * 30
    }
    init(_ area: Double) {
        length = area
    }
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
以上程序执行输出结果为:
面积为:Optional(180.0)
struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}
// origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 结构体初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 结构体初始值: \(basicRect.origin.x, basicRect.origin.y) ")
// 将origin和size的参数值赋给对应的存储型属性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))
print("Size 结构体初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 结构体初始值: \(originRect.origin.x, originRect.origin.y) ")
//先通过center和size的值计算出origin的坐标。
//然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))
print("Size 结构体初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 结构体初始值: \(centerRect.origin.x, centerRect.origin.y) ")
以上程序执行输出结果为:
Size 结构体初始值: (0.0, 0.0) 
Rect 结构体初始值: (0.0, 0.0) 
Size 结构体初始值: (5.0, 5.0) 
Rect 结构体初始值: (2.0, 2.0) 
Size 结构体初始值: (3.0, 3.0) 
Rect 结构体初始值: (2.5, 2.5) 
class SuperClass {
    var corners = 4
    var description: String {
        return "\(corners) 边"
    }
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)")
class SubClass: SuperClass {
    override init() {  //重载构造器
        super.init()
        corners = 5
    }
}
let subClass = SubClass()
print("五角型: \(subClass.description)")
以上程序执行输出结果为:
矩形: 4 边
五角型: 5 边
   * 传入无效的参数值。
   * 缺少某种所需的外部资源。
   * 没有满足特定条件。

为了妥善处理这种构造过程中可能会失败的情况。
你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面加添问号(init?)。

下例中,定义了一个名为Animal的结构体,其中有一个名为species的,String类型的常量属性。
同时该结构体还定义了一个,带一个String类型参数species的,可失败构造器。这个可失败构造器,被用来检查传入的参数是否为一个空字符串,如果为空字符串,则该可失败构造器,构建对象失败,否则成功。

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}
//通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功
// someCreature 的类型是 Animal? 而不是 Animal
let someCreature = Animal(species: "长颈鹿")
// 打印 "动物初始化为长颈鹿"
if let giraffe = someCreature {
    print("动物初始化为\(giraffe.species)")
}
以上程序执行输出结果为:
动物初始化为长颈鹿

类的可失败构造器
值类型(如结构体或枚举类型)的可失败构造器,对何时何地触发构造失败这个行为没有任何的限制。
但是,类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。

覆盖一个可失败构造器
就如同其它构造器一样,你也可以用子类的可失败构造器覆盖基类的可失败构造器。
者你也可以用子类的非可失败构造器覆盖一个基类的可失败构造器。
你可以用一个非可失败构造器覆盖一个可失败构造器,但反过来却行不通。
一个非可失败的构造器永远也不能代理调用一个可失败构造器。

可失败构造器 init!
通常来说我们通过在init关键字后添加问号的方式(init?)来定义一个可失败构造器,但你也可以使用通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!)。

析构过程

在一个类的实例被释放之前,析构函数被立即调用。用关键字deinit来标示析构函数,类似于初始化函数用init来标示。析构函数只适用于类类型。

deinit {
    // 执行析构过程
}

实例

var counter = 0;  // 引用计数器
class BaseClass {
    init() {
        counter += 1;
    }
    deinit {
        counter -= 1;
    }
}
var show: BaseClass? = BaseClass()
print(counter)
show = nil
print(counter)
以上程序执行输出结果为:1      0

可选链

可选链(Optional Chaining)是一种可以请求和调用属性、方法和子脚本的过程,用于请求或调用的目标可能为nil。
可选链返回两个值:
如果目标有值,调用就会成功,返回该值
如果目标为nil,调用将返回nil

多次请求或调用可以被链接成一个链,如果任意一个节点为nil将导致整条链失效。

使用感叹号(!)可选链实例

class Person {
    var residence: Residence?
}
class Residence {
    var numberOfRooms = 1
}
let john = Person()
//将导致运行时错误
let roomCount = john.residence!.numberOfRooms
以上程序执行输出结果为:
fatal error: unexpectedly found nil while unwrapping an Optional value

想使用感叹号(!)强制解析获得这个人residence属性numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供解析的residence值。

使用问号(?)可选链实例

class Person {
    var residence: Residence?
}
class Residence {
    var numberOfRooms = 1
}
let john = Person()
// 链接可选residence?属性,如果residence存在则取回numberOfRooms的值
if let roomCount = john.residence?.numberOfRooms {
    print("John 的房间号为 \(roomCount)。")
} else {
    print("不能查看房间号")
}
以上程序执行输出结果为:
不能查看房间号

因为这种尝试获得numberOfRooms的操作有可能失败,可选链会返回Int?类型值,或者称作"可选Int"。当residence是空的时候(上例),选择Int将会为空,因此会出现无法访问numberOfRooms的情况。
要注意的是,即使numberOfRooms是非可选Int(Int?)时这一点也成立。只要是通过可选链的请求就意味着最后numberOfRooms总是返回一个Int?而不是Int。

if ((john.residence?.printNumberOfRooms()) != nil) 
if let firstRoomName = john.residence?[0].name
if let firstRoomName = john.residence?[0].name
if let johnsStreet = john.residence?.address?.street

类型转换

类型转换
Swift 语言类型转换可以判断实例的类型。也可以用于检测实例类型是否属于其父类或者子类的实例。
Swift 中类型转换使用 is 和 as 操作符实现,is 用于检测值的类型,as 用于转换类型。
类型转换也可以用来检查一个类是否实现了某个协议。

协议

指定了类和结构需要实现的方法和变量,optional关键字可以定义协议中选择实现的方法和变量,一个结构体或者类可以实现多个协议,值得注意的是协议之间是可以继承的。

协议的语法格式如下:
protocol SomeProtocol {
    // 协议内容
}
要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。
struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 结构体内容
}
如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 类的内容
}
protocol SomeProtocol {
   init(someParameter: Int)
}

实例

protocol tcpprotocol {
   init(aprot: Int)
}
protocol AgeClasificationProtocol {
   var age: Int { get }
   func agetype() -> String
}
class Person {
   let firstname: String
   let lastname: String
   var age: Int
   init(firstname: String, lastname: String) {
      self.firstname = firstname
      self.lastname = lastname
      self.age = 10
   }
}
extension Person : AgeClasificationProtocol {
   func fullname() -> String {
      var c: String
      c = firstname + " " + lastname
      return c
   }
   
   func agetype() -> String {
      switch age {
      case 0...2:
         return "Baby"
      case 2...12:
         return "Child"
      case 13...19:
         return "Teenager"
      case let x where x > 65:
         return "Elderly"
      default:
         return "Normal"
      }
   }
}
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 协议定义
}
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 协议定义
}

实例

protocol TcpProtocol {
    init(no1: Int)
}
class MainClass {
    var no1: Int // 局部变量
    init(no1: Int) {
        self.no1 = no1 // 初始化
    }
}
class SubClass: MainClass, TcpProtocol {
    var no2: Int
    init(no1: Int, no2 : Int) {
        self.no2 = no2
        super.init(no1:no1)
    }
    // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    required override convenience init(no1: Int)  {
        self.init(no1:no1, no2:0)
    }
}
let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("res is: \(show.no1)")
print("res is: \(show.no2)")
protocol Stname {
    var name: String { get }
}
protocol Stage {
    var age: Int { get }
}
struct Person: Stname, Stage {
    var name: String
    var age: Int
}
func show(celebrator: Stname & Stage) {
    print("\(celebrator.name) is \(celebrator.age) years old")
}
let studname = Person(name: "Priya", age: 21)
print(studname)
let stud = Person(name: "Rehan", age: 29)
print(stud)
let student = Person(name: "Roshan", age: 19)
print(student)
以上程序执行输出结果为:
Person(name: "Priya", age: 21)
Person(name: "Rehan", age: 29)
Person(name: "Roshan", age: 19)
protocol HasArea {
    var area: Double { get }
}
for object in objects {
    // 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议
    if let objectWithArea = object as? HasArea {
        print("面积为 \(objectWithArea.area)")
    } else {
        print("没有面积")
    }
}
protocol AreaComputetionProtocol {
    func computeArea() -> Double
}
protocol PerimeterComputetionProtocol {
    func computePerimeter() -> Double
}
struct RectAngle: AreaComputetionProtocol, PerimeterComputetionProtocol {
    var width, height: Double

    internal func computeArea() -> Double {
        return width*height
    }

    internal func computePerimeter() -> Double {
        return 2*(width + height)
    }
}
let rect: RectAngle = RectAngle(width: 3.0, height: 4.0)
print(rect.computeArea())
print(rect.computePerimeter())

//协议继承
protocol TriangeleProtocol: AreaComputetionProtocol, PerimeterComputetionProtocol {
    var a: Double {get set}
    var b: Double {get set}
    var c: Double {get set}
    var base: Double {get set}
    var height: Double {get set}
}
struct Triangle: TriangeleProtocol {
    var a: Double = 0.0
    var b: Double = 0.0
    var c: Double = 0.0
    var base: Double = 0.0
    var height: Double = 0.0
    func computeArea() -> Double {
        return base*height/2.0
    }
    func computePerimeter() -> Double {
        return a + b + c
    }
}
let triangle: Triangle = Triangle(a: 3, b: 4, c: 5, base: 3, height: 4)
print(triangle.computeArea())
print(triangle.computePerimeter())

说到协议就不得不说委托,委托是为了让一个类或结构体能够将工作和决策交给另一个类或结构去完成。

//委托:让一个类或结构能够将工作和决策交给另一个类或结构去完成

/// 售货机协议
protocol VendingMathineProtocol {
    /// 是否投币
    var coinInserted: Bool {get set}

    /// 能否售货
    func shouldVend() -> Bool
}

/// 自动售货机类,遵守售货机协议
class Vendor: VendingMathineProtocol {
    var coinInserted: Bool = false

    /// 如果投币则售货否则不售货
    ///
    /// - Returns: 是否售货
    func shouldVend() -> Bool {
        if coinInserted {
            coinInserted = false
            return true
        }
        else
        {
            return false
        }
    }
}

/// 可乐机类
class ColaMethine {
    // 自动售货机类,遵守售货机协议
    var vendor: VendingMathineProtocol
    init(vendor: VendingMathineProtocol) {
        self.vendor = vendor
    }

    /// 投币
    func insertCoin() {
        vendor.coinInserted = true
    }

    /// 销售可乐按钮事件
    func pressColaButton() -> String {
        if vendor.shouldVend() {
            return "Here's a cola!"
        }
        else
        {
            return "You must insert coin!"
        }
    }
    /// 销售啤酒按钮事件
    func pressRootBeerButton() -> String {
        if vendor.shouldVend() {
            return "Here's a Root Beer!"
        }
        else
        {
            return "You must insert coin!"
        }
    }
}
let methine: ColaMethine = ColaMethine.init(vendor: Vendor.init())
print(methine.pressColaButton())
methine.insertCoin()
print(methine.pressColaButton())
methine.insertCoin()
print(methine.pressRootBeerButton())
print(methine.pressColaButton())

泛型

Swift 提供了泛型让你写出灵活且可重用的函数和类型。
Swift 标准库是通过泛型代码构建出来的。
Swift 的数组和字典类型都是泛型集。

不指定数据的类型,只是指定一个占位符。编译器负责对数据类型进行检查。使用方式如下:

func valueEqual<T: Equatable>(value1: T, value2: T) -> Bool {
    return value1 == value2
}

代码来看,它们功能代码是相同的,只是类型上不一样,这时我们可以使用泛型,从而避免重复编写代码。
泛型使用了占位类型名(在这里用字母 T 来表示)来代替实际类型名(例如 Int、String 或 Double)。

func swapTwoValues<T>(_ a: inout T, _ b: inout T)

swapTwoValues 后面跟着占位类型名(T),并用尖括号括起来(<T>)。这个尖括号告诉 Swift 那个 T 是 swapTwoValues(::) 函数定义内的一个占位类型名,因此 Swift 不会去查找名为 T 的实际类型。
以下实例是一个泛型函数 exchange 用来交换两个 Int 和 String 值:

// 定义一个交换两个变量的函数
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
var numb1 = 100
var numb2 = 200
print("交换前数据:  \(numb1) 和 \(numb2)")
swapTwoValues(&numb1, &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")
var str1 = "A"
var str2 = "B"
print("交换前数据:  \(str1) 和 \(str2)")
swapTwoValues(&str1, &str2)
print("交换后数据: \(str1) 和 \(str2)")
以上程序执行输出结果为:
交换前数据:  100 和 200
交换后数据: 200 和 100
交换前数据:  A 和 B
交换后数据: B 和 A
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是泛型函数的函数体部分
}

上面这个函数有两个类型参数。第一个类型参数 T,有一个要求 T 必须是 SomeClass 子类的类型约束;第二个类型参数 U,有一个要求 U 必须符合 SomeProtocol 协议的类型约束。
实例
// 非泛型函数,查找指定字符串在数组中的索引

func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            // 找到返回索引值
            return index
        }
    }
    return nil
}
let strings = ["google", "weibo", "taobao", "runoob", "facebook"]
if let foundIndex = findIndex(ofString: "runoob", in: strings) {
    print("runoob 的索引为 \(foundIndex)")
}
索引下标从 0 开始。
以上程序执行输出结果为:
runoob 的索引为 3
// Container 协议
protocol Container {
    associatedtype ItemType
    // 添加一个新元素到容器里
    mutating func append(_ item: ItemType)
    // 获取容器中元素的数
    var count: Int { get }
    // 通过索引值类型为 Int 的下标检索到容器中的每一个元素
    subscript(i: Int) -> ItemType { get }
}
// Stack 结构体遵从 Container 协议
struct Stack<Element>: Container {
    // Stack<Element> 的原始实现部分
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    // Container 协议的实现部分
    mutating func append(_ item: Element) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}
var tos = Stack<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素个数
print( tos.count)
以上程序执行输出结果为:
["google", "runoob", "taobao"]
3
protocol Container {
    associatedtype ItemType
    // 添加一个新元素到容器里
    mutating func append(_ item: ItemType)
    // 获取容器中元素的数
    var count: Int { get }
    // 通过索引值类型为 Int 的下标检索到容器中的每一个元素
    subscript(i: Int) -> ItemType { get }
}
// // 遵循Container协议的泛型TOS类型
struct Stack<Element>: Container {
    // Stack<Element> 的原始实现部分
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    // Container 协议的实现部分
    mutating func append(_ item: Element) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}
// 扩展,将 Array 当作 Container 来使用
extension Array: Container {}
func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
        
        // 检查两个容器含有相同数量的元素
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // 检查每一对元素是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        // 所有元素都匹配,返回 true
        return true
}
var tos = Stack<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
var aos = ["google", "runoob", "taobao"]
if allItemsMatch(tos, aos) {
    print("匹配所有元素")
} else {
    print("元素不匹配")
}
以上程序执行输出结果为:
匹配所有元素

访问控制

1.public 为最高级访问级别,private 为最低级访问级别。
2.元组的访问级别与元组中访问级别最低的类型一致
3.枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员单独申明不同的访问级别。
4.函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。
5.子类的访问级别不得高于父类的访问级别。比如说,父类的访问级别是internal,子类的访问级别就不能申明为public。
6.常量、变量、属性不能拥有比它们的类型更高的访问级别。
比如说,你定义一个public级别的属性,但是它的类型是private级别的,这是编译器所不允许的。
同样,下标也不能拥有比索引类型或返回类型更高的访问级别。
如果常量、变量、属性、下标索引的定义类型是private级别的,那么它们必须要明确的申明访问级别为private:
7.如果想为一个协议明确的申明访问级别,那么需要注意一点,就是你要确保该协议只在你申明的访问级别作用域中使用。
如果你定义了一个public访问级别的协议,那么实现该协议提供的必要函数也会是public的访问级别。这一点不同于其他类型,比如,public访问级别的其他类型,他们成员的访问级别为internal。
8.任何你定义的类型别名都会被当作不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。
9.常量、变量、属性、下标索引的Getters和Setters的访问级别继承自它们所属成员的访问级别。

扩展

不继承而扩展类的功能,扩展能够以非侵入式的方式,增加类、结构、甚至基本类型的行为和功能,类似于Objective_C的类别,但是强大得多。基本使用如下:
扩展就是向一个已有的类、结构体或枚举类型添加新功能。
扩展可以对一个类型添加新的功能,但是不能重写已有的功能。

// MARK: - 可乐机的扩展

extension ColaMethine {
    /// 健怡可乐按钮事件
    func pressDietColaButton() -> String {
        if vendor.shouldVend() {
            return "Here's a Diet Cola!"
        }
        else
        {
            return "You must insert coin!"
        }
    }
}
methine.insertCoin()
print(methine.pressDietColaButton())

1.扩展基本数据类型:不能在扩展中添加常规存储属性,但可以添加计算属性:值是通过计算获得的属性

let limit: Double = 1024.0
// MARK: - 为Int64扩展属性,获取存储单位的换算结果
extension Int64 {
    var K: String {return String.init(format: "%fK", Double(self)/limit)}
    var M: String {return String.init(format: "%fM", Double(self)/limit/limit)}
    var G: String {return String.init(format: "%fG", Double(self)/limit/limit/limit)}
    var T: String {return String.init(format: "%fT", Double(self)/limit/limit/limit/limit)}
}
let bytes: Int64 = 2345346457
print(bytes.K)
print(bytes.M)
print(bytes.G)
print(bytes.T)

2.mutating关键字:如果要修改自身的值而不是返回计算的结果,那么就得使用mutating关键字

// MARK: - 为Double数据类型扩展方法
extension Double {
    /// 平方值
    mutating func squra() {
        self = self*self
    }

    /// 立方值
    mutating func cube() {
        self = self*self*self
    }
}
var num: Double = 1.5
num.squra()
print(num)
num.cube()
print(num)

相等与相同

相等是指值相等,相同是指指向的对象为同一个对象。使用==来判断值的相等,用===来判断是否为同一个对象。

let a: Int = 1
let b: Int = 1
class IntClass {
    var value: Int = 0
    init(value: Int) {
        self.value = value
    }
}
let intA: IntClass = IntClass.init(value: 1)
let intB: IntClass = IntClass.init(value: 1)
print(a == b)
print(a != b)
print(intA === intB)
print(intA !== intB)

Swift内存管理

Swift以ARC方式为引用类型数据管理内存,这种方式是由编译器提供支持的。每一个引用类型的数据都有一个引用计数的的属性,当这个对象被创建的时候它的引用计数就为1,当这个对象在应用程序运行过程中被传递,就可能被多个“所有者”持有并使用。当“所有者”获得所有权时引用计算就相应加1,而在放弃所有权时引用计数就相应减1,直到引用计数为0时(所有的“所有者”都放弃了所有权),对象就会被销毁,它所占用的内存归还给内存池,供其他对象使用。

1.循环引用:循环引用就是指不同对象之间相互持有对方,这样造成了对象永远被“所有者”所占有,而无法释放,从而形成内存泄漏。

Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:
弱引用(weak)
无主引用(unowned)
弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。
对于生命周期中会变为nil的实例使用弱引用。相反的,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用。

说明:一个对象在销毁的时候会调用deinit,而一个变量赋值为nil时就会放弃对原有对象的持有。

上一篇下一篇

猜你喜欢

热点阅读