Swift5.1—委托

简介: Swift5.1—委托

委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。


下面的例子定义了两个基于骰子游戏的协议:

protocol DiceGame {
    var dice: Dice { get }
    func play()
}
protocol DiceGameDelegate {
    func gameDidStart(_ game: DiceGame)
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
    func gameDidEnd(_ game: DiceGame)
}


DiceGame 协议可以被任意涉及骰子的游戏遵循。


DiceGameDelegate 协议可以被任意类型遵循,用来追踪 DiceGame 的游戏过程。为了防止强引用导致的循环引用问题,可以把协议声明为弱引用,更多相关的知识请看 类实例之间的循环强引用,当协议标记为类专属可以使 SnakesAndLadders 类在声明协议时强制要使用弱引用。若要声明类专属的协议就必须继承于 AnyObject ,更多请看 类专属的协议


如下所示,SnakesAndLadders控制流 章节引入的蛇梯棋游戏的新版本。新版本使用 Dice 实例作为骰子,并且实现了 DiceGameDiceGameDelegate 协议,后者用来记录游戏的过程:

class SnakesAndLadders: DiceGame {
    let finalSquare = 25
    let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
    var square = 0
    var board: [Int]
    init() {
        board = Array(repeating: 0, count: finalSquare + 1)
        board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
        board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
    }
    var delegate: DiceGameDelegate?
    func play() {
        square = 0
        delegate?.gameDidStart(self)
        gameLoop: while square != finalSquare {
            let diceRoll = dice.roll()
            delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
            switch square + diceRoll {
            case finalSquare:
                break gameLoop
            case let newSquare where newSquare > finalSquare:
                continue gameLoop
            default:
                square += diceRoll
                square += board[square]
            }
        }
        delegate?.gameDidEnd(self)
    }
}


关于这个蛇梯棋游戏的详细描述请参阅 中断(Break)

这个版本的游戏封装到了 SnakesAndLadders 类中,该类遵循了 DiceGame 协议,并且提供了相应的可读的 dice 属性和 play() 方法。( dice 属性在构造之后就不再改变,且协议只要求 dice 为可读的,因此将 dice 声明为常量属性。)


游戏使用 SnakesAndLadders 类的 init() 构造器来初始化游戏。所有的游戏逻辑被转移到了协议中的 play() 方法,play() 方法使用协议要求的 dice 属性提供骰子摇出的值。


注意,delegate 并不是游戏的必备条件,因此 delegate 被定义为 DiceGameDelegate 类型的可选属性。因为 delegate 是可选值,因此会被自动赋予初始值 nil。随后,可以在游戏中为 delegate 设置适当的值。因为 DiceGameDelegate 协议是类专属的,可以将 delegate 声明为 weak,从而避免循环引用。


DicegameDelegate 协议提供了三个方法用来追踪游戏过程。这三个方法被放置于游戏的逻辑中,即 play() 方法内。分别在游戏开始时,新一轮开始时,以及游戏结束时被调用。


因为 delegate 是一个 DiceGameDelegate 类型的可选属性,因此在 play() 方法中通过可选链式调用来调用它的方法。若 delegate 属性为 nil,则调用方法会优雅地失败,并不会产生错误。若 delegate 不为 nil,则方法能够被调用,并传递 SnakesAndLadders 实例作为参数。


如下示例定义了 DiceGameTracker 类,它遵循了 DiceGameDelegate 协议:

class DiceGameTracker: DiceGameDelegate {
    var numberOfTurns = 0
    func gameDidStart(_ game: DiceGame) {
        numberOfTurns = 0
        if game is SnakesAndLadders {
            print("Started a new game of Snakes and Ladders")
        }
        print("The game is using a \(game.dice.sides)-sided dice")
    }
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
        numberOfTurns += 1
        print("Rolled a \(diceRoll)")
    }
    func gameDidEnd(_ game: DiceGame) {
        print("The game lasted for \(numberOfTurns) turns")
    }
}


DiceGameTracker 实现了 DiceGameDelegate 协议要求的三个方法,用来记录游戏已经进行的轮数。当游戏开始时,numberOfTurns 属性被赋值为 0,然后在每新一轮中递增,游戏结束后,打印游戏的总轮数。


gameDidStart(_:) 方法从 game 参数获取游戏信息并打印。game 参数是 DiceGame 类型而不是 SnakeAndLadders 类型,所以在 gameDidStart(_:) 方法中只能访问 DiceGame 协议中的内容。当然了,SnakeAndLadders 的方法也可以在类型转换之后调用。在上例代码中,通过 is 操作符检查 game 是否为 SnakesAndLadders 类型的实例,如果是,则打印出相应的消息。


无论当前进行的是何种游戏,由于 game 遵循 DiceGame 协议,可以确保 game 含有 dice 属性。因此在 gameDidStart(_:) 方法中可以通过传入的 game 参数来访问 dice 属性,进而打印出 dicesides 属性的值。


DiceGameTracker 的运行情况如下所示:

let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns


目录
相关文章
|
设计模式 Swift iOS开发
【Swift开发专栏】Swift中的协议与委托模式
【4月更文挑战第30天】Swift编程语言强调协议与委托模式。协议定义了类型需实现的方法和属性,如`SomeProtocol`示例。遵循协议的类、结构体或枚举需实现协议要求。协议可继承,也可作为类型使用。委托模式让对象间通信更灵活,通过协议实现,如`DataSourceDelegate`示例。实战案例展示了在`UITableView`和自定义下载器中使用委托模式。
457 0
|
Swift
swift UI专项训练23 设置委托和键盘返回
      前几话中我们搞定了布局,这一话我们首先要把引用全部加上,就是连线到控制器中 UIViewController { @IBOutlet weak var textField1: UITextField! @IBOutlet weak var web1: UIWebView! 然后就是代理,首先来看一下我们手机场景界面上的三个按钮: 第一个按钮是控制器本身,第二个按钮是这个界面的第一反应者,我们这个界面的第一反应者是网址这个框,第三个按钮是退出。
883 0
|
安全 编译器 Swift
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
1579 2
|
Unix 调度 Swift
苹果iOS新手开发之Swift 中获取时间戳有哪些方式?
在Swift中获取时间戳有四种常见方式:1) 使用`Date`对象获取秒级或毫秒级时间戳;2) 通过`CFAbsoluteTimeGetCurrent`获取Core Foundation的秒数,需转换为Unix时间戳;3) 使用`DispatchTime.now()`获取纳秒级精度的调度时间点;4) `ProcessInfo`提供设备启动后的秒数,不表示绝对时间。不同方法适用于不同的精度和场景需求。
1280 3
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
571 2
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
359 5
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
404 1
|
存储 移动开发 Swift
使用Swift进行iOS应用开发:探索现代移动开发的魅力
【8月更文挑战第12天】使用Swift进行iOS应用开发,不仅能够享受到Swift语言带来的简洁、快速、安全的编程体验,还能够充分利用iOS平台提供的丰富资源和强大功能。然而,iOS应用开发并非易事,需要开发者具备扎实的编程基础、丰富的实践经验和不断学习的精神。希望本文能够为您的iOS应用开发之旅提供一些有益的参考和帮助。
|
Swift iOS开发 Kotlin
苹果iOS新手开发之Swift中实现类似Kotlin的作用域函数
Swift可通过扩展实现类似Kotlin作用域函数效果。如自定义`let`, `run`, `with`, `apply`, `also`,增强代码可读性和简洁性。虽无直接内置支持,但利用Swift特性可达成相似功能。
337 7
|
调度 Swift Android开发
苹果iOS新手开发之Swift中的并发任务和消息机制
Swift的消息机制类似Android的Handler,实现任务调度有三种方式: 1. **Grand Central Dispatch (GCD)**:使用`DispatchQueue`在主线程或后台线程执行任务。 2. **OperationQueue**:提供高级接口管理`Operation`对象。 3. **RunLoop**:处理事件如输入源、计时器,类似Android的`Looper`和`Handler`。 **示例**: - GCD:在不同线程执行代码块。 - OperationQueue:创建操作并执行。 - RunLoop:用Timer添加到RunLoop中。
389 2