• Swift中的协议就是我们通常所说的接口,它连接了软件和软件模块,非常重要。首先定义一个协议: protocol CgProtocol { } 协议也有基协议: protocol CgProtocol : NSObjectProtocol { } 协议是可以多继承...

    Swift中的协议就是我们通常所说的接口,它连接了软件和软件模块,非常重要。首先定义一个协议:

    protocol CgProtocol {
        
    }
    

    协议也有基协议:

    protocol CgProtocol : NSObjectProtocol {
        
    }

    协议是可以多继承的。

    现在写一个协议方法,协议方法必须实现。比如我们写一个比较的方法

    protocol CgProtocol : NSObjectProtocol {
    
        func compare(value :Int) ->Bool
    }

    定义一个类,如果一个类要继承父类,又要遵守协议,那么父类应该放到协议之前,形式如下:

    class 类名 : 父类, 协议1,协议2 {

    //遵守协议的内容

    }

    如果接口继承了NSObjectProtocol,那么遵守这个协议的类就应该首先继承NSObejct类,因为按照接口中方法必须被实现的原则,NSObject中实现了NSObjectProtocol中的全部方法。我们实现在类ClassA中遵守接口CgProtocol中的方法:

    protocol CgProtocol : NSObjectProtocol {
    
        func compare(value : Int) ->Bool
    }
    
    class ClassA : NSObject,CgProtocol {
        func compare(value : Int) ->Bool{
            return true
        }
    }
    现在完善compare方法,代码如下:

    class ClassA : NSObject,CgProtocol {
        
        var age = 0;
        init(age:Int){
        
        self.age = age
        }
        func compare(value : Int) ->Bool{
            if age > value {
                return true
            } else {
            return false
            }
        }
    }
    
    let classA = ClassA(age: 100)
    let ret = classA.compare(50)
    println("ret is \(ret)")

    运行结果如图:

    下面来讲一下强行转换的问题,classA遵守CgProtocol协议,我们可以把classA强行转化成CgProtocol,格式如下:

    let p1 = classA as CgProtocol



    展开全文
  • swift面向协议编程

    2018-07-28 11:51:40
    一、委托模式 ...在自定义的view中如果有些函数或者属性需要到controller中去调用,委托模式的做法就是规定一个协议,让controller去遵守一个协议并提供实现,那么在自定义view中就能使用协议中的方法。  ...

    一、委托模式

     

    1、使用过程

     

    协议最常见的用法莫过于进行代理传值,这就是委托模式。常用的应用场景有:controller中自定义了一个view,view中又添加了一个自定义view。在自定义的view中如果有些函数或者属性需要到controller中去调用,委托模式的做法就是规定一个协议,让controller去遵守一个协议并提供实现,那么在自定义view中就能使用协议中的方法。 

     

    举个例子,现在想在一个controller中添加一个自定义view,可以实现点击view中按钮更改controller中label的值。简单的代码如下: 

     

    自定义view

     

    //SelectTabbar.swift
    @objc protocol SelectTabbarDelegate {
        func changeLabel(_ str: String)
    }
    

     

    //SelectTabbar.swift
     class SelectTabbar: UIView {
        var keywords : [String]?
        var buttons : [UIButton]?
        weak public var delegate : SelectTabbarDelegate?
    
        init(frame: CGRect,keywords:[String]) {
            super.init(frame: frame)
            self.keywords = keywords
            renderView()
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
        }
    
        private func renderView(){
            buttons = keywords?.enumerated().map({ (index,key) ->UIButton in
                let buttonWidth = kScreenWidth/CGFloat((keywords?.count)!)
                let button = UIButton.init(frame: CGRect.init(x: CGFloat(index)*buttonWidth, y: 0, width: buttonWidth, height: 50))
                button.setTitle(key, for: .normal)
                button.setTitleColor(UIColor.blue, for: .normal)
                button.backgroundColor = UIColor.gray
                button.tag = index
                button.addTarget(self, action: #selector(tapButton(sender:)), for: .touchUpInside)
                addSubview(button)
                return button
            })
        }
    
        @objc func tapButton(sender: UIButton){
            delegate?.changeLabel(keywords![sender.tag])
        }
    
    }
    

     

    controller:

     

     

    class TestViewController: UIViewController,SelectTabbarDelegate {
        lazy var label : UILabel = {
            var label = UILabel(frame: CGRect.init(x: 50, y: 200, width: 100, height: 30))
            label.text = labelStr
            label.backgroundColor = UIColor.red
            return label
        }()
    
        private var labelStr : String? {
            didSet{
                label.text = labelStr
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .white
            view.addSubview(label)
            setupSelectTabbar()
        }
    
        func setupSelectTabbar(){
            let selectTabbar = SelectTabbar(frame: CGRect.init(x: 0, y: kNavigationHeightAndStatuBarHeight, width: kScreenWidth, height: 50),keywords:["aa","bb"])
            selectTabbar.delegate = self
            view.addSubview(selectTabbar)
        }
        func changeLabel(_ str: String) {
            labelStr = str
        }
    
    }
    

     

    这样就能比较清楚的表明自己的逻辑。否则,如果要在view操作controller的内容,则需要在外部操作controller的实例,这就造成一个问题,就是无法操作实例中的私有属性和私有方法(虽然iOS是一门动态语言,不存在绝对的私有,但是谁会去一直去使用runtime来进行操作呢)。 

     

    2、注意点

     

    在 ARC 中,对于一般的 delegate,我们会在声明中将其指定为 weak,在这个 delegate 实际的对象被释放的时候,会被重置回 nil。这可以保证即使 delegate 已经不存在时,我们也不会由于访问到已被回收的内存而导致崩溃。ARC 的这个特性杜绝了 Cocoa 开发中一种非常常见的崩溃错误,说是救万千程序员于水火之中也毫不为过。 

     

    在 Swift 中我们当然也会希望这么做。但是当我们尝试书写这样的代码的时候,编译器不会让我们通过:

     

    'weak' cannot be applied to non-class type
    

     

    原因:这是因为 Swift 的 protocol 是可以被除了 class 以外的其他类型遵守的,而对于像 struct 或是 enum 这样的类型,本身就不通过引用计数来管理内存,所以也不可能用 weak 这样的 ARC 的概念来进行修饰。 

    两种解决方法: 

     

    1、使用@objc 

     

    2、声明类类型专属协议。通过添加 class 关键字来限制协议只能被类类型遵循,

     

    而结构体或枚举不能遵循该协议。class 关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前 

     

    protocol SelectTabbarDelegate : class

     

    二、AOP编程思想的运用

     

    首先我们理解下AOP的含义。

     

    In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a “pointcut” specification, such as “log all function calls when the function’s name begins with ‘set’”. This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code, core to the functionality. AOP forms a basis for aspect-oriented software development.

     

    在swift简单来说,就是利用协议去切入某些代码中,将额外的功能单独出来而不产生耦合,可以将这些与主逻辑关系不大的代码统一放到一起。 

     

    常用的场景:日志记录,性能统计,安全控制,事务处理,异常处理等等。 

    接上面的例子,我们需要在打开TestViewController的时候统计一次,点击两个按钮的时候也进行统计,统计的内容由identifer进行区分。 

     

    我们先建立一个Statistician.swift 来存放我们的统计逻辑。(模拟实现) 

     

    申明一个StatisticianProtocal协议并提供他的默认实现。

     

    import Foundation
    enum LogIdentifer:String {
        case button1 = "button1"
        case button2 = "button2"
        case testViewController = "testViewController"
    }
    
    protocol StatisticianProtocal {
        func statisticianLog(fromClass:AnyObject, identifer:LogIdentifer)
        func statisticianUpload(fromClass:AnyObject, identifer:LogIdentifer)
        //用一个尾随闭包来扩展功能
        func statisticianExtension(fromClass:AnyObject, identifer:LogIdentifer, extra:()->())
    }
    
    extension StatisticianProtocal{
        func statisticianLog(fromClass:AnyObject, identifer:LogIdentifer) {
            print("statisticianLog--class:(fromClass) from:(identifer.rawValue)")
        }
    
        func statisticianUpload(fromClass:AnyObject, identifer:LogIdentifer) {
            print("statisticianUpload--class:(fromClass) from:(identifer.rawValue)")
        }
    
        func statisticianExtension(fromClass:AnyObject, identifer:LogIdentifer, extra:()->()){
            extra()
        }
    }
    
    class Statistician: NSObject {
    
    }
    

     

    接下来在任何需要统计的类里面,我们让这个类去遵守这个协议,然后在需要的地方调用协议中的方法即可。如果在某个特定的类中需要调用的方法略有不同,重写协议中的方法即可。

     

    class SelectTabbar: UIView,StatisticianProtocal {
        var keywords : [String]?
        var buttons : [UIButton]?
        weak public var delegate : SelectTabbarDelegate?
    
        init(frame: CGRect,keywords:[String]) {
            super.init(frame: frame)
            self.keywords = keywords
            renderView()
            //进行一次统计
            operateStatistician(identifer: .testViewController)
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    
        override func layoutSubviews() {
            super.layoutSubviews()
        }
    
        private func renderView(){
            buttons = keywords?.enumerated().map({ (index,key) ->UIButton in
                let buttonWidth = kScreenWidth/CGFloat((keywords?.count)!)
                let button = UIButton.init(frame: CGRect.init(x: CGFloat(index)*buttonWidth, y: 0, width: buttonWidth, height: 50))
                button.setTitle(key, for: .normal)
                button.setTitleColor(UIColor.blue, for: .normal)
                button.backgroundColor = UIColor.gray
                button.tag = index
                button.addTarget(self, action: #selector(tapButton(sender:)), for: .touchUpInside)
                addSubview(button)
                return button
            })
        }
    
        @objc func tapButton(sender: UIButton){
            //进行一次统计
            switch sender.tag {
              case 0:operateStatistician(identifer: .button1)
              default:operateStatistician(identifer: .button2)
            }
            delegate?.changeLabel(keywords![sender.tag])
        }
    
        func operateStatistician(identifer:LogIdentifer){
            statisticianLog(fromClass: self, identifer: identifer)
            statisticianUpload(fromClass: self, identifer: identifer)
            statisticianExtension(fromClass: self, identifer: identifer) {
                print("extra: in SelectTabbar class")
            }
        }
    
    }
    

     

    以上代码实现了三处统计的逻辑,而不用把统计的逻辑写入controller文件中,降低了功能上的耦合度。

     

    三、用来代替extension,增强代码可读性

     

    使用扩展,可以很方便的为一些继承它的子类增添一些函数。这就带来一个问题,就是所有的子类都拥有了这个方法,但是方法的本身可能不明确,或者是只是想让少数子类来使用这个方法。这时候可以使用协议来代替extension。

     

    //定义了一个Shakable协议,遵守这个协议的类即可使用里面的方法,并为该方法提供一个默认的实现
    //where Self:UIView表明了只有uiview的子类可以遵守这个协议
    protocol Shakable {
        func shakeView()
    }
    
    extension Shakable where Self:UIView{
        func shakeView(){
            print(Self.self)
        }
    }
    

     

    这时候可以让某个子类来遵守协议。例如刚才上面的例子。

     

    class SelectTabbar: UIView,Shakable
    

     

    如果不在类中重新实现这个方法,则可以实现默认的方法。这个意思表明,SelectTabbar类的子类是遵守Shakable协议的,间接等于SelectTabbar():Shakable?。这样我们就可以愉快的让SelectTabbar对象去使用这个方法。(Self关键字只能用在协议或者类中,表示当前类,可作为返回值使用)。 

     

    一旦不想让某个子类使用shakeView()方法,很简单,只要把class SelectTabbar: UIView,Shakable中的Shakable协议干掉即可。 

     

    其他实践: 

     

    利用AOP去分离tableview的数据源和事件源的方法,可以单独处理里面的逻辑,使tableview的代理方法不显得那么冗余。 

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

         

     

     

     

     

    参考:https://github.com/PeipeiQ/MySwift

    展开全文
  • Swift 协议

    2017-09-21 16:50:11
    协议规定了用来实现某一特定功能所...语法协议的语法格式如下:protocol SomeProtocol { // 协议内容 }要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议

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

    语法

    协议的语法格式如下:

    protocol SomeProtocol {
        // 协议内容
    }

    要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。

    struct SomeStructure: FirstProtocol, AnotherProtocol {
        // 结构体内容
    }

    如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。

    class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
        // 类的内容
    }

    一、对属性的规定

    协议用于指定特定的实例属性或类属性,而不是指定是存储型属性或计算型属性。此外还必须指明是只读的还是可读可写的。
    协议中的通常用var来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,只读属性则用{ get }来表示。

    protocol classa {
    
        var marks: Int { get set }
        var result: Bool { get }
    
        func attendance() -> String
        func markssecured() -> String
    
    }
    
    protocol classb: classa {
    
        var present: Bool { get set }
        var subject: String { get set }
        var stname: String { get set }
    
    }
    
    class classc: classb {
        var marks = 96
        let result = true
        var present = false
        var subject = "Swift 协议"
        var stname = "Protocols"
    
        func attendance() -> String {
            return "The \(stname) has secured 99% attendance"
        }
    
        func markssecured() -> String {
            return "\(stname) has scored \(marks)"
        }
    }
    
    let studdet = classc()
    studdet.stname = "Swift"
    studdet.marks = 98
    studdet.markssecured()
    
    print(studdet.marks)
    print(studdet.result)
    print(studdet.present)
    print(studdet.subject)
    print(studdet.stname)

    以上程序执行输出结果为:

    98
    true
    false
    Swift 协议
    Swift

    二、对 Mutating 方法的规定

    有时需要在方法中改变它的实例。
    例如:值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。

    protocol daysofaweek {
        mutating func show()
    }
    
    enum days: daysofaweek {
        case sun, mon, tue, wed, thurs, fri, sat
        mutating func show() {
            switch self {
            case .sun:
                self = .sun
                print("Sunday")
            case .mon:
                self = .mon
                print("Monday")
            case .tue:
                self = .tue
                print("Tuesday")
            case .wed:
                self = .wed
                print("Wednesday")
            case .thurs:
                self = .thurs
                print("Wednesday")
            case .fri:
                self = .fri
                print("Wednesday")
            case .sat:
                self = .sat
                print("Saturday")
            default:
                print("NO Such Day")
            }
        }
    }
    
    var res = days.wed
    res.show()

    以上程序执行输出结果为:

    Wednesday

    三、对构造器的规定

    协议可以要求它的遵循者实现指定的构造器。
    你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体,语法如下:

    protocol SomeProtocol {
       init(someParameter: Int)
    }

    实例

    protocol tcpprotocol {
       init(aprot: Int)
    }

    四、协议构造器规定在类中的实现

    你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器。在这两种情况下,你都必须给构造器实现标上”required”修饰符:

    class SomeClass: SomeProtocol {
       required init(someParameter: Int) {
          // 构造器实现
       }
    }
    
    protocol tcpprotocol {
       init(aprot: Int)
    }
    
    class tcpClass: tcpprotocol {
       required init(aprot: Int) {
       }
    }

    施用required修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。
    如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示required和override修饰符:

    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)")

    以上程序执行输出结果为:

    res is: 20
    res is: 30
    res is: 50

    五、协议类型

    尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。
    协议可以像其他普通类型一样使用,使用场景:

    • 作为函数、方法或构造器中的参数类型或返回值类型
    • 作为常量、变量或属性的类型
    • 作为数组、字典或其他容器中的元素类型

    实例

    protocol Generator {
        associatedtype members
        func next() -> members?
    }
    
    var items = [10,20,30].makeIterator()
    while let x = items.next() {
        print(x)
    }
    
    for lists in [1,2,3].map( {i in i*5}) {
        print(lists)
    }
    
    print([100,200,300])
    print([1,2,3].map({i in i*10}))

    以上程序执行输出结果为:

    10
    20
    30
    5
    10
    15
    [100, 200, 300]
    [10, 20, 30]

    六、在扩展中添加协议成员

    我们可以可以通过扩展来扩充已存在类型( 类,结构体,枚举等)。
    扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。

    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 Classa {
        var no1: Int { get set }
        func calc(sum: Int)
    }
    
    protocol Result {
        func print(target: Classa)
    }
    
    class Student2: Result {
        func print(target: Classa) {
            target.calc(1)
        }
    }
    
    class Classb: Result {
        func print(target: Classa) {
            target.calc(5)
        }
    }
    
    class Student: Classa {
        var no1: Int = 10
    
        func calc(sum: Int) {
            no1 -= sum
            print("学生尝试 \(sum) 次通过")
    
            if no1 <= 0 {
                print("学生缺席考试")
            }
        }
    }
    
    class Player {
        var stmark: Result!
    
        init(stmark: Result) {
            self.stmark = stmark
        }
    
        func print(target: Classa) {
            stmark.print(target)
        }
    }
    
    var marks = Player(stmark: Student2())
    var marksec = Student()
    
    marks.print(marksec)
    marks.print(marksec)
    marks.print(marksec)
    marks.stmark = Classb()
    marks.print(marksec)
    marks.print(marksec)
    marks.print(marksec)

    以上程序执行输出结果为:

    学生尝试 1 次通过
    学生尝试 1 次通过
    学生尝试 1 次通过
    学生尝试 5 次通过
    学生尝试 5 次通过
    学生缺席考试
    学生尝试 5 次通过
    学生缺席考试

    八、类专属协议

    你可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。
    该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。格式如下:

    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)")

    以上程序执行输出结果为:

    res is: 20
    res is: 30
    res is: 50

    九、协议合成

    Swift 支持合成多个协议,这在我们需要同时遵循多个协议时非常有用。
    语法格式如下:

    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)

    十、检验协议的一致性

    你可以使用is和as操作符来检查是否遵循某一协议或强制转化为某一类型。

    • is操作符用来检查实例是否遵循了某个协议。
    • as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil。
    • as用以强制向下转型,如果强转失败,会引起运行时错误。

    实例

    下面的例子定义了一个 HasArea 的协议,要求有一个Double类型可读的 area:

    protocol HasArea {
        var area: Double { get }
    }
    
    // 定义了Circle类,都遵循了HasArea协议
    class Circle: HasArea {
        let pi = 3.1415927
        var radius: Double
        var area: Double { return pi * radius * radius }
        init(radius: Double) { self.radius = radius }
    }
    
    // 定义了Country类,都遵循了HasArea协议
    class Country: HasArea {
        var area: Double
        init(area: Double) { self.area = area }
    }
    
    // Animal是一个没有实现HasArea协议的类
    class Animal {
        var legs: Int
        init(legs: Int) { self.legs = legs }
    }
    
    let objects: [AnyObject] = [
        Circle(radius: 2.0),
        Country(area: 243_610),
        Animal(legs: 4)
    ]
    
    for object in objects {
        // 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议
        if let objectWithArea = object as? HasArea {
            print("面积为 \(objectWithArea.area)")
        } else {
            print("没有面积")
        }
    }

    以上程序执行输出结果为:

    面积为 12.5663708
    面积为 243610.0
    没有面积
    展开全文
  • Swift面向协议编程

    2015-08-27 16:42:27
    4.表意性极强的语句 5.可拓展性 在Swift中,前三点使用结构体和枚举就也完全可以实现。 二、在以往的面向对象编程中,只有类才能提供的 1.类的继承层次体系 2.类由于方法变量可重载所具有的可定制和重用...
    一、使用类的好处
    1.封装性
    2.抽象性
    3.采用命名空间来避免冲突
    4.表意性极强的语句
    5.可拓展性

    在Swift中,前三点使用结构体和枚举就也完全可以实现。

    二、在以往的面向对象编程中,只有类才能提供的
    1.类的继承层次体系
    2.类由于方法变量可重载所具有的可定制和重用性

    在Swift中,可定制性用结构体也可实现。

    三、类的使用代价
    1.引用类型带来的隐式共享(implicit sharing)
    为此,你有时需要不断的调用copy来给每个可变量(mutable)自己的值,而且会降低系统的运行效率,可能会导致死锁,使代码变得更加复杂,还会导致各种Bug。

    以往collectionType的绝对分享性会导致的各种问题在Swift中不会出现,因为Swift中collectionType是值类型而不是引用类型,它们不分享。

    2.你需要继承父类的一切
    它太过臃肿,所有可能相关的都被一股脑丢了进去,而且你在一开始就必须决定好你的父类,而不是后期根据需求选择。

    如果父类有存储类型的变量,你别无选择必须全部接受它们。而且要对它们进行初始化,还要注意不破坏父类的常量。

    你需要选择是否和如何以可行的方式对父类方法进行重载。

    3.类的抽象性导致类型之间的关系信息丢失(Lost Type Relationship)
    你在什么信息都不了解的时候,还必须在最初的父类中一定会被重载方法里面放一些代码。

    你需要用as! 对抽象的类型进行进一步的明确。

    四、我们需要一种更好的抽象机制
    1.支持值类型(包括类)
    2.支持静态类型关系(static type relationship)和动态派发
    4.支持可回溯的模型设计
    5.不存数据
    6.没有初始化负担
    7.明确接收者要实现什么(与类方法重载的模糊性形成对比)

    五、Swift是一门面向协议(Protocol-Oriented)的编程语言

    六、一切从协议开始
    1.避免了书写不必要的方法实现代码
    2.不再需要重载
    3.在协议方法定义中中使用self来解决类型关系丢失(Lost Type Relationship)

    protocolOrdered {
       
    func precedes(other: Self) ->Bool
    }

    4.将异质声明转化为同质

    当Ordered为Class时:

    funcbinarySearch(sortedKeys: [Ordered], forKey k:Ordered) ->Int{
       
    var lo = 0
       
    var hi = sortedKeys.count
       
    while hi > lo {
           
    let mid = lo + (hi - lo) / 2
           
    if sortedKeys[mid].precedes(k) {
                lo = mid +
    1
            }
    else {
                hi = mid
            }
        }
       
    return lo
    }

    当Ordered为Protocol时:

    funcbinarySearch<T: Ordered>(sortedKeys: [T], forKey k:T) ->Int{
       varlo =0
       
    var hi = sortedKeys.count
       
    while hi > lo {
           
    let mid = lo + (hi - lo) / 2
           
    if sortedKeys[mid].precedes(k) {
                lo = mid +
    1
            }
    else {
                hi = mid
            }
        }
       
    return lo
    }

    七、若你在Protocol 中使用Self ,Protocol 与Class 的差异会变的更大
    1.采用泛型而不是类型
    2.同质化而不是异质化的思考方式
    3.类型实例之间的交流不再意味着两个类型之间的交流
    4.采用效率更高的静态派发方式

    八、协议拓展
    语法见四天前的 Swift 2.0语法更新(二) 





    Renderer 是Protocol,TestRenderer 是满足Renderer 的结构体。
    第一张图对Renderer进行了拓展,添加了两个方法,一个是原protocol中要求的CircleAt 和新添加的RectangleAt。
    第二张图对结构体TestRenderer进行了拓展,添加了两个相同的方法。

    若以图二所示的方式定义r, CircleAt会调用extension TextRenderer中的CircleAt,而RectangleAt会调用 extension Render中的RectangleAt。

    在该种定义方式中,优先级:在原协议中被未要求的协议拓展方法 > 类型拓展方法 > 在原协议中被要求的协议拓展方法。

    九、More Protocol Extension Tricks 因水平有限暂不翻译

    十、什么时候我们该用类
    1.对实例的比较和复制没有意义的时候(例如:Window)
    2.实例的生命周期取决于外部影响的时候(例如:暂时性的文件)
    3.实例只能够被写入来影响外部状态(例如:CGContext)
    4.而且不要对抗面向对象的系统API

    十一、总结
    Protocol > Superclasses
    Extension protocol 很神奇

    英文原版视频请见WWDC Session 408https://developer.apple.com/videos/wwdc/2015/?id=408
    展开全文
  • //***********swift学习之28--协议--*************************** /*  协议规定了用来实现某一特定功能所必需的方法和属性。  任意能够满足协议要求的类型被称为遵循(conform)这个协议。  类,结构体...

    //***********swift学习之28--协议--***************************

    /*

     协议规定了用来实现某一特定功能所必需的方法和属性。

     任意能够满足协议要求的类型被称为遵循(conform)这个协议。

     类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法。

     */

    // protocol 关键字实现

    protocol FirstProtocol {

        

    }

    protocol AnotherProtocol {

        

    }


    // 遵循某个协议,需要在 类型名称 以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。

    struct SomeStructure: FirstProtocol, AnotherProtocol {

        // 结构体内容

    }


    class SomeSuperClass {

        

    }

    // 如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。

    class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {

        // 类的内容

    }





    // 协议里怎么规定属性、Mutating方法、构造器



    // 1.对属性的规定(协议跟 OC 有些区别的)

    // 协议用于指定特定的实例属性或类属性,而不用指定是存储型属性或计算型属性。此外还必须指明是只读的还是可读可写的。

    // 协议中的通常用var来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,只读属性则用{ get }来表示。

    protocol classa {

        

        var marks: Int { get set }

        var result: Bool { get }

        

        func attendance() -> String

        func markssecured() -> String

        

    }


    protocol classb: classa {

        

        var present: Bool { get set }

        var subject: String { get set }

        var stname: String { get set }

        

    }


    class classc: classb {

        var marks = 96

        let result = true

        var present = false

        var subject = "Swift 协议"

        var stname = "Protocols"

        

        func attendance() -> String {

            return "The \(stname) has secured 99% attendance"

        }

        

        func markssecured() -> String {

            return "\(stname) has scored \(marks)"

        }

    }


    let studdet = classc()

    studdet.stname = "Swift"

    studdet.marks = 98

    studdet.markssecured()


    print(studdet.marks)

    print(studdet.result)

    print(studdet.present)

    print(studdet.subject)

    print(studdet.stname)


    /*

     有时需要在方法中改变它的实例。

     例如,值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。

     */


    // 2.Mutating方法的规定

    protocol daysofaweek {

        mutating func show()

    }


    enum days: daysofaweek {

        case sun, mon, tue, wed, thurs, fri, sat

        mutating func show() {

            switch self {

            case .sun:

                self = .sun

                print("Sunday")

            case .mon:

                self = .mon

                print("Monday")

            case .tue:

                self = .tue

                print("Tuesday")

            case .wed:

                self = .wed

                print("Wednesday")

            case .thurs:

                self = .thurs

                print("Thursday")

            case .fri:

                self = .fri

                print("Friday")

            case .sat:

                self = .sat

                print("Saturday")

            default:

                print("NO Such Day")

            }

        }

    }


    var res1 = days.fri

    res1.show()



    // 3.对构造器的规定

    /*

     协议可以要求它的遵循者实现指定的构造器。

     你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体,语法如下:

     */



    // 协议构造器规定在类中的实现

    // 你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器。在这两种情况下,你都必须给构造器实现标上"required"修饰符:


    protocol tcpIPprotocol {

        init(aprot: Int)

    }


    class tcpClass: tcpIPprotocol {

        required init(aprot: Int) {

        }

    }


    // 使用required修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。

    // 如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示requiredoverride修饰符:


    protocol tcpprotocol {

        init(no1: Int)

    }


    class mainClass {

        var no1: Int // 局部变量

        init(no1: Int) {

            self.no1 = no1 // 初始化

        }

    }


    class subClassone: 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 res22 = mainClass(no1: 20)

    let show = subClassone(no1: 30, no2: 50)


    print("res22 is: \(res22.no1)")

    print("res22 is: \(show.no1)")

    print("res22 is: \(show.no2)")




    /*协议类型

     尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。

     协议可以像其他普通类型一样使用,使用场景:

     作为函数、方法或构造器中的参数类型或返回值类型

     作为常量、变量或属性的类型

     作为数组、字典或其他容器中的元素类型

     实例

     */


    protocol Generator {

        associatedtype members

        func next() -> members?

    }


    var items = [10,20,30].makeIterator()

    while let x = items.next() {

        print("ffdfdf0----\(x)")

    }


    for lists in [1,2,3].map( {i in i*5}) {

        print(lists)

    }

    //

    print([100,200,300])

    print([1,2,3].map({i in i*10}))




    /*

     在扩展中添加协议成员

     我们可以可以通过扩展来扩充已存在类型( 类,结构体,枚举等)

     扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。

     */

    protocol AgeClasificationProtocol {

        var age: Int { get }

        func agetype() -> String

    }


    class PersonTest {

        let firstname: String

        let lastname: String

        var age: Int

        init(firstname: String, lastname: String) {

            self.firstname = firstname

            self.lastname = lastname

            self.age = 10

        }

    }


    extension PersonTest : 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"

            }

        }

    }


    let persom = PersonTest(firstname:"li",lastname:"zhzh")


    print("\n\npersom-----\(persom.fullname())\n")


    print("\nagetype-----\(persom.agetype())\n\n")



    // 协议的继承

    // 协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。

    // 协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:


    protocol SomeProtocol {

        

    }


    protocol InheritingProtocol: SomeProtocol, AnotherProtocol {

        // 协议定义

    }



    protocol Classa {

        var no1: Int { get set }

        func calc(sum: Int)

    }


    protocol Result {

        func print(target: Classa)

    }


    class Student2: Result {

        func print(target: Classa) {

            target.calc(sum: 1)

        }

    }


    class Classb: Result {

        func print(target: Classa) {

            target.calc(sum: 5)

        }

    }


    class Studentbb: Classa {

        var no1: Int = 10

        

        func calc(sum: Int) {

            no1 -= sum

            print("学生尝试 \(sum) 次通过")

            

            if no1 <= 0 {

                print("学生缺席考试")

            }

        }

    }


    class Player {

        var stmark: Result!

        

        init(stmark: Result) {

            self.stmark = stmark

        }

        

        func print(target: Classa) {

            stmark.print(target: target)

        }

    }


    var marks = Player(stmark: Student2())

    var marksec = Studentbb()


    marks.print(target: marksec)


    marks.stmark = Classb()

    marks.print(target: marksec)




    /*

     类专属协议

     你可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。

     class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。格式如下:

     */


    protocol SomeInheritedProtocol {

        

    }


    protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {

        // 协议定义

    }


    protocol TcpProtocol {

        init(no1: Int)

    }


    class FathClass {

        var no1: Int // 局部变量

        init(no1: Int) {

            self.no1 = no1 // 初始化

        }

    }


    class SunClass: FathClass, 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 resff = FathClass(no1: 20)

    let showss = SunClass(no1: 30, no2: 50)


    print("\nresff is: \(resff.no1)")

    print("showss is: \(showss.no1)")

    print("showss is: \(showss.no2)\n")




    // 协议合成

    // Swift 支持合成多个协议,这在我们需要同时遵循多个协议时非常有用。

    protocol Stname {

        var name: String { get }

    }


    protocol Stage {

        var age: Int { get }

    }


    struct PersonRR: Stname, Stage {

        var name: String

        var age: Int

    }


    func show(celebrator: Stname & Stage) {

        print("\(celebrator.name) is \(celebrator.age) years old")

    }


    let student = PersonRR(name: "Roshan", age: 19)

    print(student)


    show(celebrator: student)




    /*

     检验协议的一致性

     你可以使用isas操作符来检查是否遵循某一协议或强制转化为某一类型。

     is操作符用来检查实例是否遵循了某个协议。

     as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil

     as用以强制向下转型,如果强转失败,会引起运行时错误。

     实例

     下面的例子定义了一个 HasArea 的协议,要求有一个Double类型可读的 area

     */


    protocol HasArea {

        var area: Double { get }

    }


    // 定义了Circle类,都遵循了HasArea协议

    class Circle1: HasArea {

        let pi = 3.1415927

        var radius: Double

        var area: Double { return pi * radius * radius }

        init(radius: Double) { self.radius = radius }

    }


    // 定义了Country类,都遵循了HasArea协议

    class Country: HasArea {

        var area: Double

        init(area: Double) { self.area = area }

    }


    // Animal是一个没有实现HasArea协议的类

    class Animal1 {

        var legs: Int

        init(legs: Int) { self.legs = legs }

    }


    let objects: [AnyObject] = [

        Circle1(radius: 2.0),

        Country(area: 243_610),

        Animal1(legs: 4)

    ]


    for object in objects {

        // 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议

        if let objectWithArea = object as? HasArea {

            print("面积为 \(objectWithArea.area)")

        } else {

            print("没有面积")

        }

    }



    展开全文
  • 点击上方“iOS开发”,选择“置顶公众号”关键时刻,第一时间送达! 先不说楚枫的这般年纪,能够踏入元武一重说明了什么,最主要的是,楚枫在刚刚踏入核心地带时,明明只是...
  • [译] Swift 代码格式

    2019-06-10 18:15:29
    原文地址:Swift Code Formatters 原文作者:Mattt 译文出自:掘金翻译计划 ...里面有很多 iOS 开发者,他们互相窃窃私语,讨论他们是多么得等不及苹果公司为 Swift 发布的官方风格指南和格式化程序。 ...
  • Swift4 学习笔记——高级篇
  • 本节内容转载于 ...扩展 就是为一个已有的类、结构体、枚举类型或者协议类型添加新功能。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即 逆向建模 )。扩展和 Objective-C 中的分类类似。(与 Obj...
  • Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和行控制语句。 控制流语句则...
  • Swift 类型转换 Swift 语言类型转换可以判断实例的类型。也可以用于检测实例类型是否属于其父类或者子类的实例。 Swift 中类型转换使用 is 和 as 操作符实现,is 用于检测值的类型,as 用于转换类型。 类型转换也...
  • KeyPath在Swift中的妙用

    2019-02-27 23:14:06
    原文链接: The power of key paths in Swift 自从swift刚开始就被设计为是编译时安全和静态类型后,它就缺少了那种我么经常在运行时语言中的动态特性,比如Object-C, Ruby和JavaScript。举个例子,在Object-C中,...
  • Swift 协议(Protocols)

    2016-06-27 21:42:12
    协议定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为遵循(conform)这...
  • 神奇的 Swift 错误处理

    2016-08-01 10:54:02
    原文:Magical Error Handling in Swift 作者: Gemma Barlow 译者:kmyhy Swift 中的错误处理从 O-C 沿袭而来,但 Swift 1.0 之后逐渐发生了巨大改变。重要的改变发生在 Swift 2,它率先使用了“处理非异常的...
  • Swift --**表格书写**

    2018-11-16 16:44:04
    Swift –表格书写 第一步定义表格 这里 swift的初始化与oc不太一样 但是思想是一样的 全局变量 var tableView:UITableView? //设置表格的位置大小 代理 重中之重就是表格的代理协议 &lt;UITableViewDelegate,...
  • Swift 编码规范

    2018-08-17 09:51:10
    一、swift简史 1、介绍 swift是苹果公司于2014年推出用于撰写OS和iOS应用程序的语言。它由苹果开发者工具部门总监“克里斯.拉特纳”在2010年开始着手设计,历时一年完成基本的架构。到后来苹果公司大力...
  • Swift关键字总结上篇

    2019-06-20 11:11:21
    Swift 中有多少关键字? 在Swift官方文档的词汇结构中, 有非常多的关键字, 它们被用于声明中、语句中、表达式中、类中、模式中, 还有以数字符号#开头的关键字, 以及特定上下文环境使用的关键字。 本文中涉及的代码...
  • 1.3 Swift语言演进

    2020-06-15 13:34:27
    1.3 Swift语言演进 小荷才露尖尖角,早有蜻蜓立上头 长风破浪会有时,直挂云帆济沧海 俗话说得好:要想掌握一门学科先关注它的发展史,同样的,要想学好一门语言先看它的进化史。 任何一门卓越的语言,都有其独特...
  • Swift 3 中的新特性

    2016-07-04 14:28:18
    原文:What’s New in Swift 3? 作者: Ben Morrow 译者:kmyhy Swift 3 将在下半年退出,对于 Swift 程序员来说,它带来了许多改变。 如果你没有密切关注 Swift Evolution 项目,那么在将代码迁移到 Swift 3 ...
  • Swift 协议 协议规定了用来实现某一特定功能所必需的方法和属性。 任意能够满足协议要求的类型被称为遵循(conform)这个协议。 类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。 ...
1 2 3 4 5 ... 20
收藏数 793
精华内容 317