GeekBand-Swift 第三周笔记(泛型、函数类型、闭包)

泛型的暗中同意值

                                where T:base-class                               钦定类型T必需是基类或然派生于基类

基本概念

闭包是函数类型的实例,而函数类型正是三个指南针,相像于类,栈上积存指针,指向堆上的靶子。

图片 1

函数类型内部存款和储蓄器模型.png

乍后生可畏看有一些儿犯迷糊?无妨,一个个来深入分析。
函数类型是八个指针,它在堆上有多少个对象,那五个目的也是指针。叁个叫对象指针,贰个叫函数指针。

观看此间,有个别同学或然会认为泛型很复杂,连使用其目标下的习性,都得反射,太繁杂了,还不比不用吧。

声称一个可空的int类型,由于C#语法对那些做了简化平常大家都不这么写,而是这样写:

答案

一个简短的循环引用,不表明了。

class Customer {
    let name: String
    weak var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
    let number: UInt64
    let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}
var xiaoMing: Customer?
var xiaoMingID: CreditCard?
xiaoMing = Customer(name: "xiaoMing")
xiaoMingID = CreditCard(number: 522644, customer: xiaoMing!)
xiaoMing?.card = xiaoMingID
xiaoMing = nil
xiaoMingID = nil

泛型,看名就会猜到其意义,正是泛指的类型。好比相恋的人,女子,黄人,白种人,能够泛称为【人】。

class MyGenericClass<T1, T2, T3>
{
    private T1 t1Object;

    public MyGenericClass(T1 item)
    {
        t1Object = item;
    }

    public T1 T1Object
    {
        get
        {
            return t1Object;
        }
    }
}

Weak-Strong Dance

其意气风发局地诗意的名字是用来延长弱援用对象的生存周期的,别尚未等到你要用,对象就被系统给咔嚓了。

图片 2

Weak-Strong Dance.png

上边接受了一些三十二线程知识,不详讲了,简来讲之你要知道在代码中弱引用是非常不安全的。在张开风姿罗曼蒂克体系操作时,可能等不到你要使用它,就被系统回笼了它的内部存款和储蓄器。在本例中,注释部分就是一时转换到三个强援引部分变量,未有被疏解的代码正是 withExtendedLifetime 函数了。

import UIKit
class Employee {
 var name: String
 init(name:String){
        self.name=name
        print("Employee init")
    }
func doClosure() {
        DispatchQueue.global().async { [weak self] in
        /*
            self?.process("first process")
            usleep(500)
            self?.process("second process")
            */
       /*
            if let strongRef=self {
                strongRef.process("first process")
                usleep(500)
                strongRef.process("second process")
            }*/
          withExtendedLifetime(self){
                self?.process(message: "first process")
                usleep(500)
                self?.process(message: "second process")
            }   
        }
    }
 deinit {
        print("Employee deinit")
    }
    func process(message: String) {
        print(message)
    }
}
var employee: Employee? = Employee(name:"Jason")
employee?.doClosure()
DispatchQueue.global().async {
    usleep(100)
    employee = nil
}
dispatchMain()
/* print :
Employee init
first process
second process
Employee deinit
*/

即,使用时,就不在是泛指类型,而是一定项目。

                                where T:calss                                       类约束钦赐,类型T必须是引用类型

函数类型与闭包

由此,那一个<T>就标记了,那几个类是泛型类。此中那么些T,也得以写成A,B,C,D或任何字符。

 

其三周作业

之所以要给泛型Generic的天性Name赋值,就要求赋值字符串类型的值。

这么一个泛型就OK了。

泛型约束

其少年老成怎么用,看代码:

func max<T:Comparable>(array: [T]) -> T {
    var value=array[0]
    for index in 1..<array.count
    {
        if array[index]>value {
            value=array[index]
        }
    }
    return value
}
var data1=[1,6,3,8,5,2]
var data2=[12.3, 87.6, 77.8, 20.1, 50.2]
let m1=max(array: data1) // m1 = 8
let m2=max(array: data2) // m2 = 87.6

和泛型函数特别相近,大家只是加多了三个泛型的重临值,再让函数名幕后的泛型 T 遵从 Comparable 左券,就疑似此轻巧?就那样轻便。
若果你传进去的品种信守 Comparable 左券,那您就足以恣心纵欲调用这几个函数,大家能够点步向看看 Comparable 合同都贯彻了概念了什么东西。那样,只要大家想拿该项目做什么样事,大家就让他据守什么样公约就好了,假若你随意传个自身写的类,那编写翻译器就能唤起您错了,非常常有益。

func find<T:Container>(sequence :T, item:T.ItemType)->Bool where T.ItemType: Equatable{
 for index in 0..<sequence.count{
 if(sequence[index]==item){
            return true
        }
    }
    return false
} ```
这个奇奇怪怪的东西是什么?在了解它之前,我们得先知道协议的关联类型。
#### 关联类型

protocol Container {
associatedtype ItemType //关联类型
func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}

class Stack<T>: Container {

typealias ItemType=T

var items = [T]()

func push(item: T) {
    items.append(item)
}

func pop() -> T {
    return items.removeLast()
}

func append(item: T) {
    self.push(item: item)
}

var count: Int {
    return items.count
}
subscript(i: Int) -> T {
    return items[i]
}

} ```
哇,别讲你看不懂,笔者也看晕了,怎么又是 T 又是 ItemType ,那都是些什么哟?
不要慌,先看率先个 Container,它是二个协商。其实它的乐趣如下伪码:

protocol Container<ItemType> {
 func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

是或不是部分眉目了,那不就是泛型合同呢,斯威夫特为何还要多此一举搞出贰个涉嫌类型出来?稍安勿躁,苹果那样做当然有他的平价了。
参照链接
自个儿差没多少总计下好处:

泛型的羁绊

public MyGenericClass()
{
    t1Object = default(T1);
}

函数类型实例化

那既然是二个类型,能够被赋值,那它支持什么函数给它赋值呢?

//嵌套函数
func algorithmFunction(symbol:String)-> (Double, Double)->Double{

    func add(x:Double, y:Double)->Double{
        return x+y
    }

    func minus(x:Double, y:Double)->Double{
        return x-y
    }

    func multiply(x:Double, y:Double)->Double{
        return x*y
    }

    func divide(x:Double, y:Double)->Double{
        return x/y
    }

    switch(symbol){
    case "+":
        return add
    case "-":
        return minus
    case "*":
        return multiply
    case "/":
        return divide
    default:
        return add
    }
}
var algorithm=algorithmFunction(symbol: "/")
let result4 = algorithm(600,80)   // result4 = 7.5
public class Generic
{
    public String Name;
}

public class Generic<T>
{
    public T Name;
}

先定义多个类Animal、Cow 、Chicken和SuperCow

题目

请说出下面代码存在的问题,以及改进方式。
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
    let number: UInt64
    let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}

泛型的选择

假设是值类型就赋值0,援用类型就赋值null。

泛型函数

func exchange (x: inout Int, y: inout Int)
{
    let a = x
    x = y
    y = a 
}
var a = 55
var b = 22
print("a is \(a), b is \(b).")
//输出结果:a is 55, b is 22.
exchange(x: &a, y: &b)
print("After exchange: a is \(a), b is \(b).")
//输出结果:After exchange: a is 22, b is 55. 

好,看起来很完美,然而你有未有察觉那些函数只可以交流八个整数,假使要换来小数呢?只怕你会说,那不简单,把参数类型换来Double 就好了呗。然而豆蔻年华旦您又想换换四个字符串呢?那样换来换去不是个主意,所以泛型自然现身。

func exchange<T>(x: inout T, y: inout T)
{
    let a = x
    x = y
    y = a
}
var a = "xiaoMing"
var b = "xiaoHong"
print("a is \(a), b is \(b).")
//输出结果:a is xiaoMing, b is xiaoHong.
exchange(x: &a, y: &b)
print("After exchange: a is \(a), b is \(b).")
//输出结果:After exchange: a is xiaoHong, b is xiaoMing.

咱俩把参数类型换来 T ,然后在函数名背后加二个尖括号,也写上T ,实际上你能够写上别的你想写的字符,只要前面参数类型与它保持大器晚成致就能够了。
一丢丢小改换,今后我们就可以用来沟通整数,又能够沟通字符串啦。
后天看起来貌似很科学了,但是如若您想相比较 a 和 b 大小大概会遇见一点小题目。你哪些能保险传进去的参数扶植 “<”、“>” 呢?辛亏 斯维夫特 早已想好这几个难题了,所以引进了泛型限制。

什么是限量泛型的品种呢?

类计划好了之后,大家得以起来定义大家的泛型了:

泛型

泛型是何许?轻便说来泛型便是泛指的项目,里面能够实例化任何你想要的品种。
诸如小编想写个函数调换五个数大小:

有那样苦心孤诣的同窗,心里商量就好了,假设对老驾乘员那样说,他料定会内心默默的微笑,然后对您说,你想的不易。

 

闭包

闭包正是函数类型实例,形似可用来变量、参数、重回值,他俩内部存款和储蓄器模型生机勃勃致,那干什么要叫那个为闭包呢?提起此地不能不谈谈捕获了,在谈捕获前,想先介绍下内部存款和储蓄器泄漏。
内设有构造上得以分成堆和栈,栈上的内部存款和储蓄器由系统一分配配连串回笼又高效又有益于,不用大家牵挂。但栈的空间是最为轻易的,所以必定要有堆来存放相比宏大的数码,在 Swift 中类和闭包的实例就贮存在堆上並且受到 ARC 管理。
根底数值类型、构造、枚举、元组等不受 ARC 管理。
ARC 又叫自动援用计数管理机制,在堆上的靶子只要有三个指南针指向它,它的引用计数就加蓬蓬勃勃,如图:

图片 3

援引计数暗暗表示.png

FileStream 当时的援引计数就为三,因为有四个指针指向它。当某些对象未有指针援引的时候,注解此指标没有其余利用价值了,ARC 会将引用计数降为零,並且销毁这些指标,释放所占内部存款和储蓄器。
好,近日看起来特别完备,perfect!但是,咱们应该不慢就能够发觉有个小标题:

图片 4

循环引用.png

那是多少个类,每一个类都有壹脾品质,而以此性子偏巧又针对这五个类。真是你中有自己作者中有你不可分离啊,代码大致会是如此:

import Foundation
class Computer{
    var name: String
    var display: Monitor?
    init(name:String){
        self.name=name
        print("Computer init")
    }
    deinit{
        print("Computer deinit")
    }
}
class Monitor{
    var no: Int
    var device: Computer?
    init(no:Int){
        self.no=no
        print("Monitor init")
    }
    deinit{
        print("Monitor deinit")
    }
}
var imac:Computer?
var screen:Monitor?
imac=Computer(name: "Jason's iMac")
screen=Monitor(no: 29)
imac!.display=screen
screen!.device=imac
//imac!.display=nil
imac=nil
screen=nil
/* print : 
Computer init
Monitor init */

!!是否少了点什么,析构器怎么没被调用?这就是精粹的内部存款和储蓄器泄漏了。看了地点的内部存款和储蓄器模型,相信我们对这段代码为什么会输出如此布局就精通了,这七个类对象还竞相指着呢,引用计数都为意气风发,怎么恐怕会被放出吧?
万幸,世界上的智囊早就猜到会发生这种事所以,大家有了拍卖这种办法的办法,那正是。。。手工业赋值此中八个堆对象指针为 nil !哈,开个笑话,其实那也是生机勃勃种十二分好的拍卖方法,特别灵活,下边大家来拜见更普适的议程。

图片 5

内部存储器泄漏处理办法.png

解决办法也的确简单,只要大家将中间叁个强援用指针换来弱援引就 OK 了,也等于说那些弱援用指针将不被算进 ARC 援用计数里去,那样当栈上的两个指针断掉后,八个目的循环引用自然被打破,如图:

图片 6

弱引用.png

不常大家并不想该目的可被赋值为 nil,所以大家更创办了八个无主援引,和弱援引雷同,只是无主引用证明的靶子不允许为 nil:

图片 7

无主引用.png

好了上面再来说捕获。

好比,定义时,定义了一位。但在动用时,必得精通内定,到底是白种人还是白种人。

#region Animal 虚基类 有一个name属性 Feed方法和一个虚方法MakeANoise
    //虚基类 有一个name属性 Feed方法和一个虚方法MakeANoise
    public abstract class Animal
    {
        protected string name;

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public Animal()
        {
            name = "The animal with no name";
        }

        public Animal(string newName)
        {
            name = newName;
        }

        public void Feed()
        {
            Console.WriteLine("{0} has been fed.", name);
        }

        public abstract void MakeANoise();
    }
    #endregion

//Cow Animal的子类,实现虚方法
    public class Cow:Animal
    {
        public Cow(string name) :
            base(name)
        {
        }
        public override void MakeANoise()
        {
            Console.WriteLine("{0} says 'moo!'", name);
        }
    }

//Chicken类,Animal子类
    public class Chicken:Animal
    {
        public Chicken(string name)
            : base(name)
        { }
        public override void MakeANoise()
        {
            Console.WriteLine("{0} says 'cluck'", name);
        }
    }

//Cow的子类,有一个自己的方法Fly
    class SuperCow : Cow
    {
        public SuperCow(string name) : base(name) 
        {
        }

        public void Fly()
        {
            Console.WriteLine("{0} is flying!", name);
        }

        public override void MakeANoise()
        {
            Console.WriteLine("{0} says 'I am supercow!'", name);
        }

    }

捕获

图片 8

捕获.png

此地根本要讲的是捕获生存周期小于闭包对象的参数和一些变量,因为任何的值谈不上捕获,顶多算使用而已,但是这两侧却全然分歧,我们是真着实正的复制了他们封装在有的时候对象上,何况闭包的对象指针指向该有的时候对象。还记得后边讲函数类型时小编说过不是负有函数类型的实例都会有这几个指标指针吗?
有指标指针有二种状态,一是捕获了类的实例成员,富含实例方法和实例属性;二正是捕获了参数或部分变量。第风华正茂种正是将该闭包的指标指针指向该类的实例对象,第三种则是将对象指针指向闭包本身创建的权且对象。
还要,很着重一点,那几个指标指针是强引用该对象,那样,我们恐怕知道又要出事情了。依然先看内部存款和储蓄器模型吧:

图片 9

抓获内部存款和储蓄器模型.png

有如并未什么狼狈,这我们未来思谋大器晚成种情况。笔者是叁个闭包,小编捕获了类的实例成员,那样板人的指标指针就将指向该实例对象;小编是以此类,小编定义了壹脾性能,该属性是那个闭包,那样自身的实例对象就将有贰天性质指针指向该闭包。看代码:

class Employee{
    var name: String
    var printer:(()->())?  
 /*   lazy var printer: (()->())? = {
        print("name: \(self.name)")
    } */
   init(name:String){
        self.name=name
        self.printer = {
            print("name: \(self.name)")
        }
        print("Employee init")
    }
     deinit {
     print("Employee deinit")
    }
}
var employee:Employee?
employee=Employee(name: "Jason")
employee?.printer?()
//employee?.printer=nil
employee = nil
/* print : 
Employee init
name: Jason */

同黄金时代析构器不会被调用,因为闭包和那个指标还两两相指呢,唯有当小编手动赋值该对象的闭包属性为 nil 时,析构器才会调用。看图:

图片 10

闭包循环引用.png

那湮灭办法我们应该也猜到了,相似是注明弱援用。

图片 11

弱引用.png

只放出不一样部分: self.printer = { [weak self] in print("name: \(self!.name)") }

图片 12

弱援引内存图.png

接下来,你就不曾然后了。

                                                         ——Stay hungry!Stay foolish!

泛型、函数类型、闭包

那大家要是想接收泛型对象里的天性和办法时,要如何做呢?

封锁类型

C#语法——元组类型

//继承了迭代器接口,这样方便使用Foreach 约束它的类型为Animal及其子类
    public class Farm<T>:IEnumerable<T> where T : Animal
    {
        private List<T> animals = new List<T>();

        public List<T> Animals
        {
            get 
            {
                return animals;    
            }
        }
        //迭代器
        public IEnumerator<T> GetEnumerator()
        {
            return animals.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return animals.GetEnumerator();
        }

        //执行所有animal的MakeANoise()
        public void MakeNoises()
        {
            foreach (T animal in animals)
            {
                animal.MakeANoise();
            }
        }
        //执行所有animal的Feed()
        public void FeedTheAnimals()
        {
            foreach (T animal in animals)
            {
                animal.Feed();
            }
        }
        //获得animals中的cow
        public Farm<Cow> GetCows()
        {
            Farm<Cow> cowFarm = new Farm<Cow>();
            foreach (T animal in animals)
            {
                if (animal is Cow)
                {
                    cowFarm.Animals.Add(animal as Cow);
                }
            }
            return cowFarm;
        }
    }
public static void Excute()
{
    Generic<int> gs = new Generic<int>();
    gs.Name = 518;
    Generic<Task> gsTask = new Generic<Task>();
    gsTask.Name = new Task(()=> {
        Console.WriteLine("Kiba518");
    });
}

public class Generic<T>
{
    public T Name = default(T); 
}

概念泛型类

实际应用办法如下:

T能够是大肆的标示符,只要服从命名准则就可以。

其少年老成范围就是指【where T : Base】。

class MyGenericClass<T1, T2, T3>
{
    private T1 t1Object;

    public MyGenericClass()
    {
        t1Object = new T1();
    }
}

在泛型类中,有个特地的束缚可供大家利用。

泛型定义好了,大家用写代码来调用它:

很简短,调用泛型函数的时候,钦定泛型函数的[点名项目]即可。

class MyGenericClass<T1> where T : constraint1,constraint
{
    ...
}