美文网首页
方法(一)

方法(一)

作者: 小橘子成长记 | 来源:发表于2018-05-20 15:19 被阅读0次

    在上一章中,我们学习了属性,它是结构的常数和变量。方法,是结构中的函数。

    现在,你将进一步了解方法和初始化器。与属性一样,你将开始设计更复杂的结构。在本章中学习的内容将适用于所有命名类型的方法,包括类和枚举,你将在后面的章节中看到。

    方法复习

    记得Array.removeLast()?它会删除一个数组实例的最后一个元素:

    var numbers = [1, 2, 3]
    numbers.removeLast()
    numbers // [1, 2]
    
    QQ20180518-142632@2x.png

    像removeLast()这样的方法可以帮助你控制结构中的数据。

    比较 方法和计算属性

    通过计算属性,你可以从结构内部运行代码。这听起来很像一种方法。有什么区别呢?这实际上是一种风格的问题,但有一些好的想法可以帮助你做出决定。属性包含你可以得到和设置的值,而方法执行工作。

    有时,当方法的唯一目的是返回一个值时,这种区分会变得模糊。


    QQ20180518-142928@2x.png

    问问自己,你是否希望能够设置一个值,并得到值。计算属性可以在内部设置一个setter组件来写入值。另一个需要考虑的问题是计算是否需要大量的计算或从数据库读取。即使对于一个简单的值,一个方法在调用时间和计算资源上是昂贵的。所以如果调用的代价很小(如常量时间O(1)),则使用计算属性。

    将函数转换为方法

    为了探索方法和初始化器,你创建一个简单的名为SimpleDate的模型。请注意,苹果的基础库包含一个健壮的的日期类,它能正确地处理日期和时间。

    在下面的代码中,你如何将monthsUntilWinterBreak(date:) 转换为一个方法?

    let months = ["January", "February", "March",
                  "April", "May", "June",
                  "July", "August", "September",
                  "October", "November", "December"]
    
    struct SimpleDate {
      var month: String
    }
    
    func monthsUntilWinterBreak(from date: SimpleDate) -> Int {
      return months.index(of: "December")! - months.index(of: date.month)!
    }
    

    制作一个方法就像在结构定义中移动函数一样简单:

    struct SimpleDate {
      var month: String
      func monthsUntilWinterBreak(from date: SimpleDate) -> Int {
        return months.index(of: "December")! - months.index(of: date.month)!
      }
    }
    

    方法中没有标识关键字;它实际上只是一个命名类型中的函数。使用点语法调用方法,就像处理属性一样。就像属性一样,当你开始键入一个方法名称时,Xcode将提供补全。你可以用键盘上的上下箭头键来选择一个,你可以通过按Tab自动完成:

    QQ20180518-145521@2x.png
    let  date = SimpleDate(month: "October")
    date.monthsUntilWinterBreak(from: date) // 2
    

    如果你仔细思考一下这个代码,你会发现该方法的定义是笨拙的。必须有一种方法来访问实例存储的内容,而不是将实例本身作为参数传递给方法。如果我们这样调用它更好:
    date.monthsUntilWinterBreak() // Error!

    介绍 self

    结构定义就像一个蓝图,而实例是一个真实的对象。要访问实例的值,可以在结构中使用关键字self。Swift编译器将它作为秘密参数传递给你的方法。方法定义转换为:

    // 1
    func monthsUntilWinterBreak() -> Int {
      // 2
      return months.index(of: "December")! - months.index(of: self.month)!
    }
    

    这就是改变:
    1,方法定义中没有参数。
    2,在实现中,self将替换旧的参数名。
    现在你可以调用该方法而无需传递参数:

    date.monthsUntilWinterBreak() // 2
    

    看起来干净多了! 你还可以做一件事来简化代码,消除 self .。

    self是你对实例的引用,但是大多数情况下你不需要使用它,因为如果你只是使用变量名,Swift会理解你的意图。虽然你可以一直使用self来访问当前实例的属性和方法,但大多数情况下你不需要这样做。在冬天,你可以只说month而不是self.month:

      return months.index(of: "December")! - months.index(of: month)!
    

    大多数程序员只在需要时才使用self,例如,在局部变量和同名属性之间消除歧义。稍后你会得到更多的练习。

    初始化器

    在前面的章节中,你已经了解了初始化器,但是让我们再来看看这些方法的新知识。

    初始化器是用来创建新实例的特殊方法。它们省略了func关键字甚至名称。相反,他们使用init。初始化器可以有参数,也可以不要。

    现在,当你创建SimpleDate结构的新实例时,你必须为month属性指定一个值:

    let date = SimpleDate(month: "January")
    

    你可能会发现有一个方便的无参数初始化器更有效。可以创建一个具有合理默认值的新SimpleDate实例:

    let date = SimpleDate() // Error!
    

    虽然编译器现在给你一个错误,但是你可以提供无参数初始化器。通过实现init,可以使用默认值创建最简单的初始化方法:

    struct SimpleDate {
     var month: String 
    
     init() {
        month = "January"
     }
    
     func monthsUntilWinterBreak() -> Int {
        return months.index(of: "December")! - months.index(of: month)!
     }
    }
    
    

    下面是代码中发生的事情:
    1 init()定义既不需要func关键字,也不需要名称。使用类型的名称来调用初始化器。
    2 像一个函数一样,初始化器必须有一个参数列表,即使它是空的。
    3 在初始化器中,为结构的所有存储属性赋值。
    4 初始化器永远不会返回值。它的任务只是初始化一个新实例。

    现在你可以使用简单的初始化器来创建实例:

    let date = SimpleDate()
    date.month // January
    date.monthsUntilWinterBreak() // 11
    

    您可以在初始化器中测试更改的值:

    init() {
      month = "March"
    }
    

    冬季的值将因此而改变:

    let date = SimpleDate()
    date.month // March
    date.monthsUntilWinterBreak() // 9
    

    考虑到这里的实现,良好的用户体验优化将初始化器使用今天日期作为默认值。

    以后,你能够检索当前日期,可以使用Foundation库中的Date类来处理日期。在你获得这些库提供的所有功能之前,让我们继续实现你自己的SimpleDate类型。

    结构的初始化

    初始化器确保实例在准备使用之前设置所有属性:

    struct SimpleDate {
      var month: String
      var day: Int
    
      init() {
        month = "January"
        day = 1 
      }
    
      func monthsUntilWinterBreak() -> Int {
        return months.index(of: "December")! - months.index(of: month)!
      }
    }
    

    如果你试图在不设置day属性的情况下创建初始化器,那么编译器会抱怨。

    通过创建一个自定义初始化器,你放弃了使用自动成员初始化器的选项。回想一下,自动生成的成员初始化器接受所有的属性作为参数,比如SimpleDate结构的init(month:day:)。当你编写一个自定义初始化器时,编译器会将自动创建的初始化丢弃。

    所以这段代码现在无法运行:

    let valentinesDay = SimpleDate(month: "February", day: 14) // Error!
    

    然后, 你必须定义自己的初始化器参数:

    init(month: String, day: Int) {
      self.month = month
      self.day = day
    }
    

    在该代码中,你将传入的参数分配给结构的属性。注意,self是如何用来告诉编译器你在引用属性而不是本地参数。

    但是这在简单的初始化器中是没有必要的:

    init() {
      month = "January"
      day = 1 
    }
    

    在该代码中,没有具有与属性相同名称的任何参数。因此,编译器不需要self来理解你所指的属性。

    有了这个初始化器,你可以像调用自动生成的初始化器一样调用新的初始化器:

    let valentinesDay = SimpleDate(month: "February", day: 14)
    valentinesDay.month // February
    valentinesDay.day // 14
    

    相关文章

      网友评论

          本文标题:方法(一)

          本文链接:https://www.haomeiwen.com/subject/xqgudftx.html