Menu
Woocommerce Menu

Swift入门基础6,iOS开发系列

0 Comment


前言

在软件工程中,设计模式(Design
Pattern)
是对软件设计中普遍存在的各种问题,所提出的解决方案。

策略模式(Strategy
Pattern)
中定义一系列算法,并将每一个算法封装起来,使使它们可以相互替换,策略模式让算法独立于它的客户而变化。

下面我会就iOS实际开发中的case,
举例说明Strategy模式的在开发中的具体应用。

在开发中, 我们会遇到各种表单的提交需求,
在实际需求中,表单提交之前一般都需要检查一下所填写的数据的有效性。这些对于特定类型的数据的有效性检查,可能在程序中被反复用到。下面我就以开发中必不可少的用户登录功能,来看看Strategy模式的具体应用。

以email方式登录为例。你至少需要检查email和password输入的有效性。在修改email或者修改password时,
你又需要使用email和password数据有效性的检查逻辑。对于这种多于一次使用逻辑,基于DRY(Don’t
Repeat Youself)
原则,我们需要封装起来。

图片 1用户登录页面

  • 首先我们把检测数据的行为抽象一下,定义一个Validator
    Protocol。Validator我们可以称为策略的抽象

protocol Validator { func isValid(text: String?) -> Bool}
  • 有策略的抽象,那策略的实现在哪里。ok,实现来了。我们定义EmailValidator类,让它来实现Validator

class EmailValidator: Validator { func isValid(text: String?) -> Bool { let regEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" if let text = text { let entryTest = NSPredicate(format: "SELF MATCHES %@", regEx) return entryTest.evaluate(with: text) } return false }}
  • 另一个PasswordValidator类

class PasswordValidator: Validator { func isValid(text: String?) -> Bool { let regEx = "[A-Z0-9a-z._%+-;/><#]{6,30}" if let text = text { let entryTest = NSPredicate(format: "SELF MATCHES %@", regEx) return entryTest.evaluate(with: text) } return false }}
  • 这时候我们需要一个ValidatorManager来根据用户的条件,选择调用哪个策略的实现。

enum ValidationType: Int { case Email = 0 case Password}class ValidationManager { private lazy var validators: [Validator] = [EmailValidator(), PasswordValidator()] func validationForText(text: String?, withValidationType validationType: ValidationType) -> Bool { return validators[validationType.rawValue].isValid(text: text) }}
  • 在LoginViewController具体的调用时这样子的:

 private lazy var validationManager = ValidationManager() @IBAction func tapLoginButton(_ sender: UIButton) { let emailValidation = validationManager.validationForText(text: emailField.text, withValidationType: .Email) let passValidation = validationManager.validationForText(text: passwordField.text, withValidationType: .Password) if emailValidation && passValidation { // Log in successfully and enter main view performSegue(withIdentifier: "ShowMainView", sender: self) } else { // validation failed. You can do anything you want to alert user. let alert = UIAlertController(title: "Failed", message: "Email or password is not valid.", preferredStyle: .alert) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) alert.addAction(cancelAction) present(alert, animated: true, completion: nil) } }
可以扩展类的什么:

添加新的属性,且也不能为已有的属性添加属性观察器;添加新方法;添加新的构造器;甚至还可以在该扩展中使该类符合某个协议,即申明该类遵守某个协议。

注意:扩展仅可以为类添加新的功能,但不能重写该类原有的方法等。某类的扩展是可以访问该类的属性方法的。

如果运行修改后的代码片段,可以发现按钮的高度会马上根据约束而改变,而不会看到修改前的变化动画。

具体代码:StrategyPatternDemo

Swift中的扩展,对应OC中的分类,可以用它来拓展类的功能。

在讨论 setNeedsLayout 和 layoutIfNeeded 之前,我们需要普及下主运行循环

总结

  • 第一步:你需要把你要做的事情抽象,用protocol来表示。 – 策略的抽象
  • 第二步:你要做的事情在不同情况下肯定会有不同具体实现,创造不同类来实现你的protocol。 –
    策略的具体实现
  • 第三布:创建一个Manager来统一调配你的具体策略的实现。这个Manager会开放一个接口,满足你调用的最小充要条件。Manager会根据你的条件,决定具体使用什么策略。
  • 最后一步:在你需要的地方使用Manager来实现你要做的事情吧。
方法要求:

协议中定义对方法的要求。只有方法的定义,没有对应的方法体。

protocol SomProtocol { var fullName: String {get set} func nameHandle(fullName: String) -> String}

推荐阅读iOS开发者注意, ATS 出没!

构造器要求:

协议中也可以定义对构造器的要求。基本和定义方法是一样。但是在实现该协议的类中实现该构造器时,无论在类中是作为指定构造器还是便利构造器,都要在前面加上required关键字。

protocol SomProtocol { var fullName: String {get set} init(fullName: String) func nameHandle(fullName: String) -> String}

class StaffModel: UserModel, SomProtocol { var fullName: String = "" // SomProtocol协议要求的属性 required init(fullName: String) { // 协议要求的构造器 self.fullName = fullName super.init() } func nameHandle(fullName: String) -> String { // 协议要求的方法 return "fullName:\" }}

layoutIfNeeded()
会强制马上更新布局和显示,由于该方法是同步的,视图框架的移动会被动画模块捕获,如果运行上述代码片段,能看到按钮视图的高度变化动画。

扩展语法:

extension Int就表示这是对Int类型的扩展。

extension Int { var numStr: String { get{ return "\次" } } func printTheNum() { print } func repeats->Void) { for _ in 0..<self { task() } }}

Int的扩展中,我们首先为Int添加了只读计算型属性numStr,它是以该Int型的数值拼接成的字符串。然后又新添了两个方法。printTheNum()方法的功能是打印numStr属性。repeats方法有一个参数task,其类型为()->Void,即没有参数没有返回值的闭包!传入该方法的闭包将会被执行该数值次。调用:

 let num = 2 num.printTheNum() // log:2次 num.repeats { print("i am your man") }// log: i am your man// i am your man

当用户和应用交互时,和这些交互相关的事件由系统自动产生并且借助UIKit设定的特殊端口传递给应用。事件在应用内部以队列的形式存在并且逐个的被分发到应用的主运行循环去执行。UIApplication是第一个接收事件的对象,并且决定需要如何处理事件,如触控事件通常被分发到应用的主窗口对象,并且最终分发到发生该触控事件的视图上面。

可变实例方法:

如果在扩展中新添的方法修改了self或者self的属性,则必须以关键字mutating标注。

 mutating func squre() { self = self*self }

什么是泛型?

通俗说一下,就是一个函数,或者类,使用到某个参数,这个参数的类型,在设计期间是可供多选的,那么这个函数,就称作泛型函数。

泛型约束:

泛型约束就是指定这个泛型使用到的可选类型的选择范围——也就是说指定它必须有的某些特征,因为泛型函数里需要用到这些特征,所以调用方使用泛型时候,指定的可选参数类型也必须具有这些特征。

数字和字典这些集合类就是典型的“泛型”,这些集合可以存放很多类型的数据。如果没有泛型,那想象一下,我们得定义多少种数组初始化的方式。

协议是什么无需再做解释,直接来看Swift中它的语法:

下面结合代码来在看一下 setNeedsLayout 和 layoutIfNeeded 区别。

通过协议实现协议

有时不方便在原类中实现协议,那我们通过扩展来实现协议可以达到一样的效果。

更多请参考:Swift中协议的简单介绍

图片 2

协议语法:
protocol SomProtocol { // 这里是协议要定义的东西}

在Swift中表示类实现协议和继承是一样的,都用冒号:,若实现多个协议则多个协议之间同样也用逗号隔开。当一个类既有继承父类,又实现了某协议时。规定冒号后面先是继承的类,然后才是实现的协议。

下面的代码定义了一个StaffModel类,它继承于UserModel,并且还实现了两个协议SomProtocol,OtherProtocol

class StaffModel: UserModel, SomProtocol, OtherProtocol {}

然后把方法 layoutIfNeeded 替换为 setNeedsLayout, 更新后的动画模块如下:

属性要求:

协议中可以定义对所遵守该协议的类中属性的要求。可以要求属性的类型、名字,以及可读还是可写。

下面定义的协议SomProtocol中定义了所遵守该协议的类中属性的要求。必须要有个叫fullName的,字符串类型的,可读可写的属性。

protocol SomProtocol { var fullName: String {get set} }

视图调用方法setNeedsLayout
是想告诉系统当前视图以及所有子视图需要在更新周期重新布局和重画,对于视图操作一个异步行为,因为这个方法会马上完成并返回,而不会等待布局更新和重画完成后再返回,至于更新周期发生时间也是未知的。

标签:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章

网站地图xml地图