Swift3.0学习笔记-Protocols

https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID267


       Protocol在Swift中的作用就是接口, Protocol中可以声明若干个方法,但没有函数体; protocol跟Java interface关键字的作用是完全一样的。 Swift protocol跟Java interface的区别是interface可以给成员变量赋值、而protocol对成员变量只能声明set/get方法。

       前文提到Swift只支持单继承, 但可以实现若干个protocol


Swift声明protocol(即接口)的语法:

protocol SomeProtocol {
    //声明函数
}


结构体可以实现一个或多个protocol(即接口):

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 结构体实现protocol
}

类同样可以实现一个或多个protocol, 跟Java的extends、implements关键字不同的是, Swift在继承类实现接口时只用逗号分隔,语法如下:

class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
    // 类定义
}
    SomeClass继承于SomeSuperclass,并实现了FirstProtocol和AnotherProtocol。


成员属性:

       protocol可以声明成员变量或者静态成员变量,但不能对成员变量赋值, 只能声明其get或set方法(注意仅仅声明,没有实现)。 成员变量可以声明set/get方法或者get方法, 但不能只声明set方法;即成员变量是可读写或者只读变量。PS:Xcode会给出提示。
protocol SomeProtocol {
    var mustBeSettable: Int { get set }   //可读写
    var doesNotNeedToBeSettable: Int { get }  //只读
}

       对于静态变量,当类去实现protocol时可以用class或者static声明为静态变量。
protocol AnotherProtocol {
    static var someTypeProperty: Int { get set }  //声明为可读写静态属性
}

protocol FullyNamed {
    var fullName: String { get }  //声明为只读属性,属性类型为String
}
注意: 使用结构体实现protocol时,Xcode会自动加入其属性; 使用类型实现protocol时,Xcode会自动加入该属性并赋初值。
struct Person: FullyNamed {
    var fullName: String
}

class PersonExt: FullyNamed {
    var fullName: String = ""
}
     在实现protocol FullyNamed后, 结构体Person和类PersonExt必须包含protocol声明的成员属性。

class StarShip: FullyNamed {
    var prefix: String?   //Optional类型
    var name: String   //字符串类
    init(name: String, prefix: String? = nil) {  //跟其它语言一样当后面参数有默认值时,在调用时可以省略
        self.name = name
        self.prefix = prefix
    }
    
    var fullName: String {  //protocol要求返回fullName的值
        return (prefix != nil ? prefix! + " " : "") + name  //三目运算符跟Java的用法一致!
    }
}

var objNc = StarShip(name: "Enterprise", prefix: "USS")
var objNc1 = StarShip(name: "Company")
类StarShip实现了FullyNamed, 即要对fullName返回String类型的值, 声明了成员变量prefix、name和构造函数init。 因为init对参数prefix赋了初值nil,所有在调用时可以省略。(注意:Swift的String肯定有值,String?可能为nil或者字符串; 所以String?类似于Java的String)


成员方法: Swift同Java一样对方法的访问权限设置了关键字: public/internal/private,  作用类似于Java修饰方法的public/protected/private。在Swift语法中,函数访问级别默认为internal, 声明函数时可以省略internal关键字。
protocol SomeProtocol {
    static func someTypeMethod()
}
protocol RandomNumberGenerator {
    func random() -> Double
}
      跟Java interface一样, Swift声明函数但不带函数体, 在实现类中定义函数功能。
protocol FullyNamed {
    var fullName: String { get }
    
    func getName() -> String
    
    static func getFullName() -> String
}

class PersonExt: FullyNamed {
    static func getFullName() -> String {  //函数访问权限都是internal,所有可以省略internal
        return "PersonExt full"
    }
    
    internal func getName() -> String {  // 访问权限public/internal/private中默认的internal
        return "personExt name"
    }
    var fullName: String = ""  //等号后是字符串,就是fullName的get方法实现。
    
}

var person = PersonExt()
print(person.getName())  //通过实例调用方法
print(PersonExt.getFullName())  //通过类调用
输出:

personExt name

PersonExt full



修改成员属性的接口函数:
在《Swift3.0学习笔记-Functions》中提到Swift的函数体默认不能修改成员属性, 声明函数时必须添加前缀mutating关键字才可以, 该特性同样适用于protocol。   如果想在接口函数中修改成员属性, 那么该接口函数一定要添加mutating前缀
    注意: 如果类实现protocol, 那么函数不用带mutating前缀, 默认可以修改成员属性; 结构体和枚举类型如果要在接口函数中修改成员属性,那么必须添加mutating前缀。基本语法如下:
protocol Togglable {
    mutating func toggle()    //如果结构体和枚举要实现Togglable,那么必须添加mutating前缀
}

示例代码:Shop的buySomeThing方法没有mutating前缀,但可以修改DoShop类的amount属性;Togglable的toggle方法带有mutating前缀,可以修改name属性。
protocol Togglable {
    mutating func toggle()  //测试修改成员属性
}

struct TestStruct: Togglable {
    mutating internal func toggle() {
        name = "TestStruct modify success"
    }
    var name: String
}

protocol Shop {
    func buySomeThing()
}

class DoShop: Shop {
    func buySomeThing() {
        amount -= 50    //测试修改成员属性amount
    }

    var amount: Int = 100
    
}

var objStruct = TestStruct(name: "TestStruct")
var objShop = DoShop()
objStruct.toggle()  //修改成员属性name的值
print(objStruct.name)
objShop.buySomeThing()  //修改成员变量amount
print("amount: \(objShop.amount)")
输出:

TestStruct modify success

amount: 50



声明构造函数接口:在protocol声明若干个init方法, 在类实现Protocol时定义函数体。 
protocol SomeProtocol {
    init(someParameter: Int)
}

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // initializer implementation goes here
    }
}
    在类实现构造函数时 必须添加required关键字;仅仅在类被声明为final类型, 那么可以省略required关键字。

如果基类、Protocol声明了一模一样的构造函数, 派生类在继承基类并实现接口时会怎样? PS:同样场景在Java里寻址会找到接口函数。

protocol SomeProtocol {
    init()
}
 
class SomeSuperClass {
    init() {
        // initializer implementation goes here
    }
}
 
class SomeSubClass: SomeSuperClass, SomeProtocol {
    // "required" from SomeProtocol conformance; "override" from SomeSuperClass
    required override init() {
        // initializer implementation goes here
    }
}
说明:感觉Swift的这个语法就是和稀泥, 基类和protocol的init都要兼顾, 在派生类里构造函数添加前缀required override。

现在是重点了, 我们知道Java里的interface是将一个接口的引用作为参数传给一个对象。  在Swift里,protocol可以是类成员变量的类型。

class DoShop: Shop {
    required init(amount: Int) {
        self.amount = amount
    }

    func buySomeThing() {
        amount -= 50    //测试修改成员属性amount
    }

    var toggle: Togglable   //protocol类型
    var amount: Int = 100
}

扩展实现接口, 即Extension实现protocol。

protocol TextRepresentable {
    var textualDescription: String { get }
}

extension Dice: TextRepresentable {   //删掉protocol名称并实现接口函数也可以!
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}

Protocol支持多继承, 类要实现所有protocol里的函数。语法和示例代码:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // protocol definition goes here
}
protocol A {
    func methodA()
}
protocol B {
    func methodB()
}
protocol C: A,B {
    func methodC()
}
class MulTest: C {
    internal func methodB() {
        ...
    }

    internal func methodA() {
       ...
    }

    internal func methodC() {
        ...
    }
}


前面提到protocol适用于类、结构体和枚举, 那么如果只想给类用该怎么办呢?

Swift支持仅适用于类的Protocol, 语法如下:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // class-only protocol definition goes here
}
示例代码:

protocol A {
    func methodA()
}
protocol B: class {
    func methodB()
}
protocol C: class,A,B {
    func methodC()
}
class MulTest: C {
    internal func methodB() {
        ...
    }

    internal func methodA() {
       ...
    }

    internal func methodC() {
       ...
    }
}


复合Protocol, 因为protocol可以继承若干个protocol, 那么如何判断它到底继承于哪些protocol呢?  Swift提供了关键字&。

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) { //参数是  Named & Aged 类型
    print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21) //Person类实现了Named和Aged的方法
wishHappyBirthday(to: birthdayPerson)
// 输出 "Happy birthday, Malcolm, you're 21!"
说明:语法跟Java类似, 函数参数类型为接口, 但在调用函数时传递的是对象引用(实现了接口)。


类型判断: is、as?和as!关键字同样适用于Protocol,语法跟判断类是一样的。


小结:

Swift的protocol可以理解为接口类, 它主要作用就是声明它的能力,即声明若干个函数, 作用类似于Java的interface关键字。

1、 Protocol默认适用于类、结构体和枚举, 添加后缀class后只给类用;

2、类可以实现若干个protocol;

3、protocol可以作为类成员变量的参数类型(同Java的回调用法);

4、Swift支持多个protocol作为参数类型;

5、protocol声明init函数后,在类中实现该init函数时必须带required前缀。




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值