2017-06-27 17:33:32 u012903898 阅读数 4859

协议是swift一个重要的部分,类似于Java中的接口,但是还不是很一样。相比较OC,swift中协议更加灵活,它可以应用在很多场景,使整个项目的框架结构更加易于延展。

一、什么场景下使用协议

协议与类类似,可以被继承,当继承某个协议之后就要给协议所定义的属性赋值并且实现协议中的方法。

既然协议与类这么类似,那我们为什么不全部用类来实现,为什么还要用到协议?
举个简单的例子,有一只猫和狗,他们都属于宠物,用类去实现就要这样操作,定义一个父类叫做宠物,里面有喂食和玩耍两个方法,猫和狗都继承与宠物这个父类。这样操作自然可以实现,但是要知道,猫和狗不都是宠物,这里把宠物定义成父类就不是很合适,这里应该把宠物定义成协议就相对合适很多啦

二、协议的使用方法

1、协议定义

// 协议定义通过关键字protocol
protocol SomeProtocol {
    // 协议定义
}

// 协议可以继承一个或者多个协议
protocol SomeProtocol2 :SomeProtocol {
    // 协议定义
}

// 结构体实现协议
struct SomeStructure : SomeProtocol,SomeProtocol2 {
    // 结构体定义
}

// 类实现协议和继承父类,协议一般都写在父类后面
class SomeSuperclass {
    // 父类定义
}

class SomeClass :SomeSuperclass,SomeProtocol,SomeProtocol2 {
    // 子类定义
}

2、协议的属性

协议不指定是否该属性应该是一个存储属性或者计算属性,它只指定所需的属性名称和读写类型。属性要求总是声明为变量属性,用var关键字做前缀。

protocol ClassProtocol {
    static var present:Bool { get set }    // 要求该属性可读可写,并且是静态的
    var subject :String { get }            // 要求该属性可读
    var stname :String { get set }         // 要求该属性可读可写
}

// 定义类来实现协议
class MyClass :ClassProtocol {
    static var present = false       // 如果没有实现协议的属性要求,会直接报错
    var subject = "Swift Protocols"  // 该属性设置为可读可写,也是满足协议要求的
    var stname = "Class"

    func attendance() -> String {
        return "The \(self.stname) has secured 99% attendance"
    }

    func markSScured() -> String {
        return "\(self.stname) has \(self.subject)"
    }
}

// 创建对象
var classa = MyClass()
print(classa.attendance())     // 结果:The Class has secured 99% attendance
print(classa.markSScured())    // 结果:Class has Swift Protocols

3、协议普通方法实现

协议可以要求指定实例方法和类型方法被一致的类型实现。这些方法被写为协议定义的一部分,跟普通实例和类型方法完全一样,但是没有大括号或方法体。可变参数是允许的,普通方法也遵循同样的规则,不过不允许给协议方法参数指定默认值。

// 定义协议,指定方法要求
protocol RandomNumberGenerator {
    func random() -> Double    // 实现该协议,需要实现该方法
}

class LinearCongruentialGenerator :RandomNumberGenerator {
    var lastRandom = 42.0
    let m = 139968.0
    let a = 3877.0
    let c = 29573.0

    // 实现协议方法
    func random() -> Double {
        lastRandom = ((lastRandom * a + c) % m)
        return lastRandom / m
    }
}

let generator = LinearCongruentialGenerator()
print("随机数:\(generator.random())")          //结果:随机数: 0.37464991998171
print("另一个随机数:\(generator.random())")     //结果:另一个随机数: 0.729023776863283

4、协议中实现构造函数

协议SomeProtocol中不光可以声明方法/属性/下标,还可以声明构造器,但在Swift中,除了某些特殊情况外,构造器是不被子类继承的,所以SomeClass中虽然能够保证定义了协议要求的构造器,但不能保证SomeClass的子类中也定义了协议要求的构造器。所以我们需要在实现协议要求的构造器时,使用required关键字确保SomeClass的子类必须也得实现这个构造器。

protocol TcpProtocol {
    // 初始化构造器要求
    init(aprot :Int)
}

class TcpClass :TcpProtocol {
    var aprot: Int
    // 实现协议的初始化要求时,必须使用required关键字确保子类必须也得实现这个构造器
    required init(aprot: Int) {
        self.aprot = aprot
    }
}

var tcp = TcpClass(aprot: 20)
print(tcp.aprot)   // return:20

三、 使用实例

开头说的宠物猫和宠物狗的例子,利用协议可以这样实现,声名个动物的父类,然后让猫和狗class都继承与动物class。在定义一个宠物的属性,里面有玩耍和喂食两个方法,让猫和狗都继承与宠物协议,实现代码如下:

protocol Pet {
    func playWith()
    func fed(food : String)

}

class Animal{


    var name : String = ""
    var birthPlace : String = ""

    init(name: String,birthPlace:String) {

        self.name = name
        self.birthPlace = birthPlace
    }
}


class Dog: Animal, Pet{

    func playWith() {

        print("狗狗在玩")
    }

    func fed(food: String) {

        if food == "骨头" {

            print("狗狗Happy")
        }
        else {

            print("狗狗Sad")
        }
    }


}

class Cat: Animal, Pet {

    func playWith() {


        print("猫猫在玩")
    }

    func fed(food: String) {

        if food == "鱼" {

            print("猫猫Happy")
        }
        else {

            print("猫猫Sad")
        }
    }
}


let dog = Dog(name:"狗狗小黑", birthPlace: "北京")

dog.playWith()

dog.fed(food:"骨头")

let cat = Cat(name:"猫猫小白", birthPlace:"上海")

cat.playWith()

cat.fed(food: "鱼")

注意:同时继承父类和协议的时候,父类要写在前面

四、typealias与协议结合的使用

typealias 的作用是给类型进行扩展,它与协议放在一起使用会碰撞出不一样的火花

1、typealias的基本使用

extension Double {

    var km : Length{ return self * 1000.0 }

    var m : Length{ return self }

    var cm : Length{ return self / 100 }


}

这里对Double类型进行扩展

let runDistance:Length = 3.14.km //3140

2、typealias结合协议使用

定义一个协议,代表重量,但是它的类型要根据继承与它的类或结构体来定义,协议代码如下:

protocol WeightCalculable {

    associatedtype WeightType

    var weight:WeightType{get}

}

这里weight属性的类型就抛了出来,便于继承协议的类或结构体来定义

class iPhone7 : WeightCalculable {

    typealias WeightType = Double

    var weight: WeightType {

        return 0.114
    }

}

class Ship : WeightCalculable {

    typealias WeightType = Int

    let weight: WeightType

    init(weight: Int) {

        self.weight = weight
    }


}

这里定义了两个类,一个是iPhone7,一个是Ship,都继承于协议WeightCalculable,但是weight的类型大不相同。
iPhone7的weight属性是Double类型的,Ship的weight属性是Int类型的。

extension Int {

    typealias Weight = Int
    var t:Weight  {
        return 1_000*self
    }


}

let ship = Ship(weight:4_637.t)

最后这段代码,用于扩充Int类型,自定义了t字段来代表吨

五、系统协议的使用

我们还可以继承于系统协议来定义系统方法,这里简单极少介绍三种常用系统协议

1、Equatable协议用于自定义”==”来实现操作

class Person:Equatable , Comparable, CustomStringConvertible {

    var name:String
    var age:Int

    init(name:String,age:Int) {

        self.name = name
        self.age = age
    }

    var description: String {

        return"name: "+name + ",age:" + String(age)
    }

}

func == (left: Person, right: Person) ->Bool{

    return left.name == right.name && left.age == right.age

}

let personA = Person(name:"a",age:9)

let personB = Person(name:"a",age:10)

personA == personB

personA != personB

注意:func == 方法要紧跟协议下面写,否则编译器会报错

2、Comparable协议用于自定义比较符号来使用的

func <(left: Person, right: Person) ->Bool{


        return left.age < right.age

}

let personA = Person(name:"a",age:9)

let personB = Person(name:"a",age:10)

注意,在定义比较符号后,很多方法也会同时修改便于我们使用,例如排序方法

let person1 = Person(name:"a",age:9)

let person2 = Person(name:"a",age:12)

let person3 = Person(name:"a",age:11)

var  arrPerson = [person1,person2,person3]

arrPerson .sort()

//此时arrPerson : [person1,person3,person2]

3、CustomStringConvertible协议用于自定义打印

class Person:Equatable , Comparable, CustomStringConvertible {

    var name:String
    var age:Int

    init(name:String,age:Int) {

        self.name = name
        self.age = age
    }

    var description: String {

        return"name: "+name + ",age:" + String(age)
    }

}

重写description 讲自定义打印格式return出来

print(person1)

//name: a,age:9

协议是swift非常重要的一部分,苹果甚至为了它单独出来——–面向协议编程,利用协议的优点和灵活性可以使整个项目结构更加灵活,拥有更加易于延展的架构。

2019-07-27 16:22:28 weixin_42433480 阅读数 28

扩展(Extension)

  • Swift中的扩展,有点类似于OC中的分类(Category) 
  • 扩展可以为枚举、结构体、类、协议添加新功能
  • 可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等
  • 扩展不能办到的事情:要注意扩展不能改变原有类的内存结构
  1. 不能覆盖原有的功能 
  2. 不能添加存储属性,不能向已有的属性添加属性观察器 (添加存储属性会改变原有类的内存结构)
  3. 不能添加父类 (由于有可能会继承父类的存储属性,会改变内存结构,所以也不行)
  4. 不能添加指定初始化器,不能添加反初始化器,因为它们只能写在原有类里面,所以不能写在扩展里
  5. ...

计算属性、下标、方法、嵌套类型

  • Extension添加计算属性

  • Extension添加下标

下面的例子是为了防止数组越界:

var arr: Array<Int> = [10, 20, 30]
print(arr.startIndex) //0
print(arr.endIndex) //3 endIndex表示最后一位元素的下一位

extension Array {
    subscript(nullable idx: Int) -> Element? {
        if(startIndex..<endIndex).contains(idx) {
            return self[idx]
        }
        return nil
    }
}

print(arr[nullable: 1] ?? 0)  //20
  • Extension添加嵌套类型
extension Int {
    func repeatitions(task: () -> Void) {
        for _ in 0..<self {task()}
    }
    
    mutating func square() -> Int {
        self = self * self;
        return self
    }
   //Int中嵌套了一个新的枚举类型
    enum Kind {case negative, zero, positive}
    var kind: Kind {
        switch self {
        case 0: return .zero
        case let x where x > 0: return .positive
        default: return .negative
        }
    }
    
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex { decimalBase *= 10}
        return(self / decimalBase) % 10
    }
}

3.repeatitions {
    print(1)
}// 1  1  1

var b = 10
print(b.square()) // 100

var c = -10
print(c.kind) // negative

var d = 456
print(d[1]) //5 d[1]表示取十位数字 d[0]为6

协议、初始化

  • extension添加协议

 添加Equatable协议和便捷初始化器

  • extension添加初始化器

如果希望自定义初始化器的同时,编译器也能够生成默认初始化器,可以在扩展中编写自定义初始化器

  •  required初始化器也不能写在扩展中

 


协议

  • 如果一个类型已经实现了协议的所有要求,但是还没有声明它遵守了这个协议 ,可以通过扩展来让它遵守这个协议
protocol TestProtocal {
    func test()
}
class TestClass {
    func test() {
        print("test")
    }
}

extension TestClass : TestProtocal { }
  •  编写一个函数,判断一个整数是否为奇数?

 UInt和Int都属于BinaryInteger类型

  • 扩展可以给协议提供默认实现,也间接实现『可选协议』的效果 
  •  扩展可以给协议扩充『协议中从未声明过的方法』
protocol TestProtocal {
    func test1()
}

extension TestProtocal {
    func test1() {
        print("TestProtocal test1")
    }
    func test2() {
        print("TestProtocal test2")
    }
}

class TestClass: TestProtocal { }

var cls = TestClass()
cls.test1() //TestProtocal test1
cls.test2() //TestProtocal test2

var cls2: TestProtocal = TestClass()
cls2.test1() //TestProtocal test1
cls2.test2() //TestProtocal test2

class TestClass1: TestProtocal {
    func test1() { print("TestClass test1")}
    func test2() { print("TestClass test2")}
}
var cls3 = TestClass1()
cls3.test1() //TestClass test1
cls3.test2() //TestClass test2

var cls4: TestProtocal = TestClass1()
cls4.test1() //TestClass test1
cls4.test2() //TestProtocal test2 这里是因为遵守TestProsal协议,并且TestProsal协议里没有test2方法,编译器就会默认遵守协议的类的实例里没有test2方法,就会去扩展里去找

泛型

class Stack<E> {
    var elements = [E]()
    
    func push(_ element: E) {
        elements.append(element)
    }
    func pop() -> E {
       return elements.removeLast()
    }
    func size() -> Int {
        return elements.count
    }
}

extension Stack {
    func top() -> E {
        return elements.last!
    }
}

extension Stack: Equatable where E: Equatable {
    static func == (left: Stack, right: Stack) -> Bool{
       return left.elements == right.elements
    }
}

var sta1 = Stack<Int>()
sta1.push(10)
sta1.push(20)
sta1.push(30)

var sta2 = Stack<Int>()
sta2.push(10)
sta2.push(20)
sta2.push(30)

print(sta1 == sta2) //true

 

 

 

 

 

 

 

 

 

 

 

 

2016-12-17 22:37:22 heyufei 阅读数 2515

Swift中对象序列化的实现

  在swift中要使某个类可以序列化,只需要类实现NSCoding协议,并实现协议中的一个必要的构造函数和一个方法,分别对应序列化和反序列化的二个过程。

//提供一个解码器解码数据,通过解码数据来初始化类变量
required init?(coder aDecoder: NSCoder){ } 

//提供一个编码器编码数据
func encodeWithCoder(aCoder: NSCoder){ } 


示例:
假设我们有如下类:其中的属性updateUrl为需要序列化和反序列化到User Default中

class AppParams{
    var updateUrl: String? //
}

继承NSObject,实现NSCoding协议:

class AppParams: NSObject, NSCoding{
...
}


实现序列化和反序列化函数
就是实现NSCodeing协议

//反序列化
required convenience init?(coder aDeCoder: NSCoder){
      self.updateUrl = aDeCoder.decodeObject(forKey: "updateUrl") as? String
}

    //序列化
func encode(with aCoder: NSCoder) {
    aCoder.encode(updateUrl, forKey: "updateUrl")
}

保存和读取

//保存到user default
let userDefault = UserDefaults.standard
let data = NSKeyedArchiver.archivedData(withRootObject: value as Any)
userDefault.setValue(data, forKey: Preferences.KEY_LAST_APP_PARAMS)

//从user default中读取        
let userDefault = UserDefaults.standard            
guard let data = userDefault.value(forKey: Preferences.KEY_LAST_APP_PARAMS) else{return nil}
return NSKeyedUnarchiver.unarchiveObject(with: data as! Data) as? AppParams
2016-10-07 22:41:00 u011525168 阅读数 7

Swift3.0 - 真的很简单
Swift3.0 - 数据类型
Swift3.0 - Array
Swift3.0 - 字典
Swift3.0 - 可选值
Swift3.0 - 集合
Swift3.0 - 流控制
Swift3.0 - 对象和类
Swift3.0 - 属性
Swift3.0 - 函数和闭包
Swift3.0 - 初始化和释放
Swift3.0 - 协议protocol
Swift3.0 - 类和结构体的区别
Swift3.0 - 枚举
Swift3.0 - 扩展
Swift3.0 - 下标
Swift3.0 - 泛型
Swift3.0 - 异常错误
Swift3.0 - 断言
Swift3.0 - 自动引用计数(strong,weak,unowned)
Swift3.0 - 检测API
Swift3.0 - 对象的标识
Swift3.0 - 注释
Swift3.0 - 元类型
Swift3.0 - 空间命名
Swift3.0 - 对象判等
Swift3.0 - 探究Self的用途
Swift3.0 - 类簇
Swift3.0 - 动态调用对象(实例)方法
Swift3.0 - 文本输出
Swift3.0 - 黑魔法swizzle
Swift3.0 - 镜像
Swift3.0 - 遇到的坑

学习目标

  • swift中的协议和OC 中协议的区别
  • swift中的协议的用法
  • 怎么使用

swift和Object-C 协议的区别

1.OC 中的协议只能用于类,swift中的协议不仅能用于类,还能用于结构体和枚举
2.OC 中的协议可以设置可选实现,但Swift中的协议必须实现
3.Swift中的协议增加了一个关键字mutating可以决定结构体和枚举实现协议的时候,是否可以修改属性的值


Swift协议(protocol)

  • 定义一个协议
protocol LoveMusic{
    func songMusic()
    var  simpleDescription: String { get }
    mutating func modifyMusic(name:String)
}
  • 协议可以继承协议(多个)
protocol LoveMusic:Love,Skill{
    func songMusic()
    var  simpleDescription: String { get }
    mutating func modifyMusic(name:String)
}
  • 协议的重写
protocol Love{
    var name:String{get set} 
}
 // 我们重写协议中的属性,让其只要get方法即可
protocol LoveMusic:Love{
    func songMusic()
    var  simpleDescription: String { get }
    mutating func modifyMusic(name:String)
}

注意方法不能重写

  • 可以实现多个协议
class Student:Love,Skill{
internal var name: String = ""
internal func modifyMusic(name: String) {
}
}
  • 协议类型的变量
// 协议的 标准写法
var prptocol:ProtocolA
var delegate:ProtocolA & ProtocolB & ProtocolC
  • 协议做函数参数
    func getStudent(student: ProtocolA & ProtocolB){
        student.dreak()
        student.eat()
  }
  • 实现协议可选函数或者变量
  1. 在协议的前面加上关键字 @objc ,再在你需要设置可选方法或者属性的前面加上关键字@objc 和 optional 即可

示例代码:

@objc protocol OptionalProtocol{
 @objc optional func optionalMethod()
 @objc optional var name:String {set get}
}

2.还有另外一种方法可以实现可选协议就是遵守NSObjectProtocol

示例代码:

public protocol SCNSceneRendererDelegate : NSObjectProtocol {
    optional public func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
}

你应该注意的

  • 在协议中定义属性的时候,必须使用set get \ get\ 修饰,但不能只用set修饰,swift要求我们属性必须有get方法

  • 类不能多次继承同一个协议

 class Student:Love,Love{
    internal var name: String
    internal func modifyMusic(name: String) {
    }
}
  • 如果多个协议中有相同的方法和属性,只需要实现一次即可
protocol Love{
    var name:String{get set}
    mutating func modifyMusic(name:String)
}
protocol Skill{
    var name:String{get set}
    mutating func modifyMusic(name:String)
}
class Student:Love,Skill{
    internal var name: String = ""
    internal func modifyMusic(name: String) {
  }
}

一般人不知道

  • 需求:创建一个协议只能被类遵守
 protocol MyClassDelegate: class{
    func getName()
 }

什么时候必须使用类协议?

当你要使用weak 修饰的时候

class UDP{
    weak var delegate: UDPDelegate?
}
protocol UDPDelegate:class{
}

swift类

阅读数 293

没有更多推荐了,返回首页