Swift中的面向对象编程

苹果公司的大多数框架都具有面向对象的体系结构。 在开始研究iOS / MacOS开发之前,您应该首先了解面向对象的编程和设计模式。 在本文中,我们将介绍基本概念和设计模式,以帮助您开始进行应用程序开发。

总览

面向对象编程(OOP)是一种编程范式,代表了具有数据字段(描述对象的属性)和相关方法(称为方法)的“对象”的概念。 对象通常是类的实例,用于彼此交互以设计应用程序和计算机程序。

面向对象编程包含3个关键方面:

封装意味着对象将其状态信息保密。 其他对象不是直接处理对象的数据,而是以消息的形式向对象发送请求,对象可能通过更改其内部状态来响应其中的一些请求。

多态性意味着不同类别的对象可以互换使用。 这一点特别重要,因为它允许您以后以不必要的方式挂钩类,而不必事先预料这些类是何时设计的。

继承意味着一类的对象可以从另一类(基类或父类)派生其行为的一部分。 某些面向对象的语言(例如,C ++,但不支持Swift)允许多重继承,其中一类对象可以从多个独立的基类派生其部分或全部行为。

类和对象

在面向对象的编程中,类是用于创建对象,提供状态(成员变量)和行为实现(成员函数,方法)的初始值的可扩展程序代码模板。 换句话说,类就像一个蓝图,它定义了类型的数据和行为。

  按钮{ 
}

上面的定义创建一个名为Button的空类。 它唯一能做的就是创建新的Button对象:

  var button = Button() 

在上面的示例中, buttonButton类的实例。

物产

类和实例可以具有名为属性的关联值。

 广场广场{ 
var length :Int = 1
}

Square类的length属性的默认值为1

为了创建length不同于1正方形,我们需要编写一个自定义初始化程序。

 广场广场{ 
var length :Int = 1
  初始化长度 :整数){ 
self.length =长度
}
}
  var firstSquare = Square( 长度 :3) 
println(firstSquare.length)
  var secondSquare = Square( 长度 :10) 
println(secondSquare.length)
  如果 firstSquare.length < secondSquare.length { 
println(“小方块的长度为\(firstSquare.length)”)
} 其他 {
println(“小方块的长度为\(secondSquare.length)”)
}

方法

方法将行为添加到类和实例。

 广场广场{ 
var length :Int = 1

  func area() -> Int { 
返回长度*长度
}
}

调用时, area()方法计算Square实例的面积。 要调用方法,请使用. 符号。

  var square = Square(5) 
  println(square.area()) //打印25 
  sqare.length = 10 
  println(square.area()) //打印100 

类属性

属性的通常用法是实例属性,例如Square的长度。 您也可以拥有一个属性。 下面是使用类属性的示例。 Tank类具有一个bonusDamage的计算属性,用于在升级时增加所有坦克的伤害。 升级完成后,我们需要做的就是调用Tank.upgrade() ,所有Tank实例将遭受更大的破坏。

 坦克 { 
class var bonusDamage :Double {
返回 Double(Upgrade.level) * 2.5
}
   baseDamage = 10.0 
  var 伤害 :双倍{ 
返回 self.baseDamage + Tank.bonusDamage
}
   func upgrade(){ 
升级等级+ = 1
}
  结构升级{ 
静态无功水平= 0
}
}
  var tank = Tank() 
  println(容器损坏) 
// 10.0
  Tank.upgrade() 
  println(容器损坏) 
// 12.5
  Tank.upgrade() 
  println(容器损坏) 
// 15.0

遗产

一个类可以从另一个类继承方法,属性和其他特征。 当一个类从另一个类继承时,继承的类称为subclass ,而其继承的类称为其superclass 。 继承是一种基本行为,可将Swift中的类与其他类型区分开。

继承的简单示例:

  AClass  { 
func doSomething(){
println(“来自AClass的Hello”)
}
}
  class 子类 :AClass { 
}
   base_object = AClass() 
base_object.doSomething()
//> AClass您好

   Enhanced_object = Subclass() 
Enhanced_object.doSomething()
//> AClass向您问好

覆写

您可以重写方法以提供自定义行为。 要覆盖方法,请在方法声明之前编写override关键字:

  AClass  { 
func doSomething(){
println(“来自AClass的Hello”)
}
}
  class 子类 :AClass { 
覆盖 func doSomething(){
println(“来自子类的Hello”)
}
}
   base_object = AClass() 
base_object.doSomething()
//> AClass您好

   Enhanced_object = Subclass() 
Enhanced_object.doSomething()
//>您好,来自子类

您可以使用super关键字从超类调用任何方法。

  ... 
  class 子类 :AClass { 
覆盖 func doSomething(){
super.doSomething()
  println(“来自子类的Hello”) 
}
}
   Enhanced_object = Subclass() 
Enhanced_object.doSomething()
//> AClass您好
//>您好,来自子类

基类

不继承自另一个类的类称为base class ,例如:

  用户{ 
var 名称 :字符串
var age :整数
  init名称 :字符串, 年龄 :整数){ 
self.name =名称
自我年龄=年龄
}
}

iOS和Mac OS类通常直接或间接地从NSObject继承。 如果您有混合代码库,我鼓励您在创建新类时对NSObject进行子类化:

Swift类 NSObject的 子类

  • 本身就是Objective-C类
  • 使用objc_msgSend()调用(大多数)方法
  • 提供(大多数)方法实现的Objective-C运行时元数据

不是 NSObject 子类 Swift类

  • 是Objective-C类,但仅实现了少数几种方法以实现NSObject兼容性
  • 不要将objc_msgSend()用于对其方法的调用(默认情况下)
  • 不为其方法实现提供Objective-C运行时元数据(默认情况下)

在Swift中对NSObject进行子类化可以为您带来Objective-C运行时的灵活性,以及​​Objective-C的性能。 如果不需要Objective-C的灵活性,避免使用NSObject可以提高性能。

stackoverflow Swift本机基类或NSObject

通讯协定

协议以类似于类的方式声明。

  协议 MyFirstProtocol { 
// 我什么都不做
}

协议描述了特定任务所需的方法,属性和其他要求。 例如, UITableViewDelegate协议列出了可用于响应用户事件和配置表视图的所有方法。

注意:您可以使用@optional关键字将方法标记为可选方法。 UITableViewDelegate中的所有方法都是可选的。 当您不使用@optional关键字时,该方法是必需的。 如果类符合协议且未实现所需的方法,则swift编译器将引发错误。

作为类的定义的一部分,类可以通过将其名称放在用冒号分隔的类型名称之后来遵循协议。 可以列出多个协议,并用逗号分隔:

  class AnotherSwiftClass :MyFirstProtocol, AnotherProtocol { 
...
}

如果该类继承自另一个类,请确保将超类名称放在协议列表之前。

  class AnotherSwiftClass :AClass,MyFirstProtocol,AnotherProtocol { 
...
}

注意:协议使用与普通方法相同的语法,但不允许为方法参数指定默认值。

代表图案

委派是iOS中过度使用的设计模式之一。 一个类可以一部分职责委派给另一个类的实例。 通过定义封装委托职责的协议来实现此设计模式,以确保符合类型(称为委托)可以提供已委托的功能。

这是一个示例,Player类将射击逻辑委托给武器:

  协议可定位的 { 
var life :Int { 获取 }
func takeDamage( 伤害 :整数)
}
  协议 Shootable { 
功能射击( 目标 :可定向)
}
  手枪  :可射击{ 
功能射击( 目标 :可定位 ){
target.takeDamage(1)
}
}
  Shot弹枪 :可射击{ 
功能射击( 目标 :可定位 ){
target.takeDamage(5)
}
}
  敌人 类别 :可定位{ 
var life :Int = 10
  func takeDamage( 损害 :内部){ 
生命-=伤害
println(“敌人丢失\(损坏)生命值”)
  如果生活<= 0 { 
println(“敌人现在死了”)
}
}
}
  玩家{ 
var 武器 :可射击
  初始化武器 :可射击){ 
self.weapon =武器
}
  功能射击( 目标 :可定位 ){ 
武器射击(目标)
}
}
  var终结者= Player( 武器 :手枪()) 
  var敌人=敌人() 
  terminator.shoot(敌人) 
//>敌人失去1点生命值
terminator.shoot(敌人)
//>敌人失去1点生命值
terminator.shoot(敌人)
//>敌人失去1点生命值
terminator.shoot(敌人)
//>敌人失去1点生命值
terminator.shoot(敌人)
//>敌人失去1点生命值
  //因为手枪效率低下而更换武器 
terminator.weapon = Shotgun()
  terminator.shoot(敌人) 
//>敌人失去5点生命
//>敌人已经死了

多态性

多态性意味着“具有多种形式”。 如果不同类的对象具有相同的superclass类,则可以互换使用。

这是一个简单的示例,其中多个实例可以用作GraphicObject

   GraphicObject { 
  func draw(){ 
println(“什么都不做”)
}
}
  SpaceShip  :GraphicObject { 
}
  class EmpireSpaceShip :SpaceShip { 
覆盖 func draw(){
println(“绘制帝国太空飞船”)
}
}
  RebellionSpaceShip  :SpaceShip { 
覆盖 func draw(){
println(“绘制叛乱太空船”)
}
}
  class DeathStar :GraphicObject { 
覆盖 func draw(){
println(“绘制死亡之星”)
}
}
  var spaceShips = [EmpireSpaceShip(),RebellionSpaceShip(),DeathStar()] 
  for spaceShip in spaceShips { 
spaceShip.draw()
}

该程序将输出:

 draws an empire space ship 
draws a rebellion space ship
draws the Death Star

单例模式

有时,一个类只有一个实例很重要。 例如,在一个系统中,应该只有一个窗口管理器(或只有一个文件系统,或者在iPhone上只有一个运动管理器)。 为了迅速实现这种效果,我们将使用class属性公开单例实例。

 单例 { 
struct Static {
静态 let实例= Singleton()
}
   var sharedInstance :单例{ 
返回 Static.instance
}
}
  Singleton.sharedInstance 

要么:

  进口基金会 
   DispatchSingleton { 
struct Static {
静态 var OnceTokendispatch_once_t = 0
静态 var 实例 :DispatchSingleton =
}
   var sharedInstance :DispatchSingleton { 
dispatch_once( Static.onceToken){
静态实例= DispatchSingleton()
}
返回 Static.instance
}
}
  DispatchSingleton.sharedInstance 

模型视图控制器

我发现最好的解释是来自Apple:

模型-视图-控制器( MVC )设计模式在应用程序中分配对象,这是三个角色之一:模型,视图或控制器。 该模式不仅定义了对象在应用程序中扮演的角色,还定义了对象之间的通信方式。 三种类型的对象中的每一种都通过抽象边界与其他对象分开,并跨这些边界与其他类型的对象进行通信。 在应用程序中,某些MVC类型的对象的集合有时称为“层”,例如模型层。

MVC对于可可应用程序的良好设计至关重要。 采用这种模式的好处很多。 这些应用程序中的许多对象倾向于更可重用,并且它们的接口倾向于更好地定义。 具有MVC设计的应用程序也比其他应用程序更容易扩展。 此外,许多可可技术和体系结构都基于MVC,并且要求您的自定义对象扮演MVC角色之一。

模型对象

模型对象封装了特定于应用程序的数据,并定义了处理和处理该数据的逻辑和计算。 例如,模型对象可能代表游戏中的角色或地址簿中的联系人。 一个模型对象可以与其他模型对象具有一对多关系,因此有时应用程序的模型层实际上是一个或多个对象图。 在将数据加载到应用程序后,属于应用程序持久状态的大部分数据(无论该持久状态存储在文件还是数据库中)都应驻留在模型对象中。 由于模型对象代表与特定问题领域相关的知识和专长,因此可以在相似的问题领域中重用它们。 理想情况下,模型对象应该与显示其数据并允许用户编辑该数据的视图对象没有显式连接-不应与用户界面和表示问题有关。

通信 :视图层中创建或修改数据的用户操作通过控制器对象进行通信,并导致创建或更新模型对象。 当模型对象发生更改时(例如,通过网络连接接收到新数据),它将通知控制器对象,该控制器对象将更新相应的视图对象。

查看物件

视图对象是用户可以看到的应用程序中的对象。 视图对象知道如何绘制自身,并且可以响应用户的操作。 视图对象的主要目的是显示来自应用程序模型对象的数据,并允许对该数据进行编辑。 尽管如此,视图对象通常在MVC应用程序中与模型对象分离。

由于您通常会重用和重新配置它们,因此视图对象可在应用程序之间提供一致性。 UIKit和AppKit框架都提供视图类的集合,而Interface Builder在其Library中提供了许多视图对象。

通信 :视图对象通过应用程序的控制器对象了解模型数据的变化,并将用户启动的更改(例如,在文本字段中输入的文本)通过控制器对象传递给应用程序的模型对象。

控制器对象

控制器对象充当一个或多个应用程序视图对象与其一个或多个模型对象之间的中介。 因此,控制器对象是一个通道,视图对象通过该通道了解模型对象的变化,反之亦然。 控制器对象还可以为应用程序执行设置和协调任务,并管理其他对象的生命周期。

通信 :控制器对象解释在视图对象中执行的用户操作,并将新的或更改的数据传递到模型层。 当模型对象更改时,控制器对象会将新的模型数据传达给视图对象,以便它们可以显示它。

挑战性

  • 创建Shape基类,并从中派生CircleSquareRectangleShape应该具有两个都返回0的area() -> Floatperimeter() -> Float两个方法。实现这些方法并在CircleSquareRectangle上添加必要的属性。
  • 在第一个练习中创建相同的类,但这一次使Shape成为协议
  • CircleSquareRectangle创建一个自定义初始化器,并分别创建两个实例。
  • 将上面练习中创建的实例添加到阵列shapes并使用多态性找出shapes的总面积和周长
  • 使用地图解决上述练习并减少
  • 在“可Targetable协议中增加armor属性,并减少敌人受到的伤害。 Obs:您需要将life属性类型更改为Float才能执行此操作。

未来阅读

  • 要更深入地了解设计模式,请阅读设计模式:可重用的面向对象软件的元素

参考文献

  • 为什么是鸭子? —多态性和设计模式简介
  • 苹果的可可核心竞争力模型-视图-控制器

来自