Swift_Apprentice_v2.0语法中

  • 六、第六部分:Structures

  • 1.Structures(结构体)

  • 举个栗子

let restaurantLocation = (2, 4)
let restaurantRange = 2.5
// Pythagorean Theorem # $
func distance(from source: (x: Int, y: Int), to target: (x: Int, y: Int))
-> Double {
        let distanceX = Double(source.x - target.x)
        let distanceY = Double(source.y - target.y)
        return sqrt(distanceX * distanceX + distanceY * distanceY)
}
func isInDeliveryRange(location: (x: Int, y: Int)) -> Bool {
        let deliveryDistance = distance(from: location, to: restaurantLocation)
        return deliveryDistance < restaurantRange
}
let restaurantLocation = (2, 4)
let restaurantRange = 2.5
let otherRestaurantLocation = (7, 8)
let otherRestaurantRange = 1.5
// Pythagorean Theorem # $
func distance(from source: (x: Int, y: Int), to target: (x: Int, y: Int))
-> Double {
        let distanceX = Double(source.x - target.x)
          let distanceY = Double(source.y - target.y)
          return sqrt(distanceX * distanceX + distanceY * distanceY)
}
func isInDeliveryRange(location: (x: Int, y: Int)) -> Bool {
          let deliveryDistance =
    distance(from: location,
               to: restaurantLocation)
          let secondDeliveryDistance =
    distance(from: location,
               to: otherRestaurantLocation)
          return deliveryDistance < restaurantRange ||secondDeliveryDistance < otherRestaurantRange
}
  • Your first structure

struct Location {
        let x: Int
        let y: Int 
}
  • Your second structure

struct DeliveryArea {
         var range: Double
         let center: Location
}
  • 完整示例:

struct Location {
    let x: Int
    let y: Int
}
struct DeliveryArea {
    var range: Double
    let center: Location
}
func distance(from source: (x: Int, y: Int), to target: (x: Int, y: Int))
    -> Double {
        let distanceX = Double(source.x - target.x)
        let distanceY = Double(source.y - target.y)
        return sqrt(distanceX * distanceX + distanceY * distanceY)
}
let areas = [
          DeliveryArea(range: 2.5, center: Location(x: 2, y: 4)),
          DeliveryArea(range: 4.5, center: Location(x: 9, y: 7))
]
func isInDeliveryRange(_ location: Location) -> Bool {
          for area in areas {
              let distanceToStore =
              distance(from: (area.center.x, area.center.y),
                 to: (location.x, location.y))
              if distanceToStore < area.range {
                 return true
              }
         }
         return false
}
let customerLocation1 = Location(x: 8, y: 1)
let customerLocation2 = Location(x: 5, y: 5)
print(isInDeliveryRange(customerLocation1)) // false
print(isInDeliveryRange(customerLocation2)) // true

  • 七、第七部分:Properties

  • 1.stored properties(存储属性)

struct Contact {
        var fullName: String
        var emailAddress: String
}
 var person = Contact(fullName: "Grace Murray",
                 emailAddress: "grace@navy.mil")
 let name = person.fullName // Grace Murray
let email = person.emailAddress // grace@navy.mil
  • Default values

struct Contact {
        var fullName: String
        var emailAddress: String
        var type = "Friend"
}
  • 2.Computed properties(计算属性)

  • Getter,举个栗子

struct TV {
      var height: Double  // 存储属性
      var width: Double   // 存储属性
      // 1
      var diagonal: Int {  // 计算属性
            // 2
            let result = sqrt(height * height + width * width)
            // 3
            let roundedResult = result.rounded()
            // 4
            return Int(roundedResult)
        }
}
 var tv = TV(height: 53.93, width: 95.87)
let size = tv.diagonal // 110
 tv.width = tv.height // 改变width,diagonal通过计算发生变化
let diagonal = tv.diagonal // 76
  • Getter and setter

var diagonal: Int {
      // 1
      get { // 2
        let result = sqrt(height * height + width * width)
        let roundedResult = result.rounded(.toNearestOrAwayFromZero)
        return Int(roundedResult)
    } set {
        // 3
        let ratioWidth = 16.0
        let ratioHeight = 9.0
        // 4
        height = Double(newValue) * ratioHeight /
      sqrt(ratioWidth * ratioWidth + ratioHeight * ratioHeight)
        width = height * ratioWidth / ratioHeight
    }
}
tv.diagonal = 70 // 通过设置tv.diagonal,自动获得tv.height和tv.width
let height = tv.height // 34.32...
let width = tv.width // 61.01...
      - ###说明:
1。因为你想要包括一个setter,你现在必须明确哪些计算组成的getter和setter,所以你周围每个代码块和花括号之前得到或设置。这不是只读属性,计算所需的单一代码块隐式getter。
2。您可以使用与前面相同的代码来获得计算值。
3。对于setter,你通常需要做出这样的假设。在这种情况下,您为屏幕比提供了一个合理的默认值。
4。考虑到一个高和宽,给定一个对角和一个比,面积有点深。你可以用一点时间来解决它们,但我已经为你做了一些脏活,并在这里提供了它们。关注的重点是:
        - newValue常量允许您使用在赋值期间传递的任何值。
        - 记住,对角线是一个整数,所以在计算中使用它时,你必须先把它转换成双精度。
        - 一旦你做了计算,你就会分配电视结构的高度和宽度。
  • Type properties(类型属性)

struct Level {
        static var highestLevel = 1 // 类型属性
        let id: Int
        var boss: String
        var unlocked: Bool
}
let level1 = Level(id: 1, boss: "Chameleon", unlocked: true)
let level2 = Level(id: 2, boss: "Squid", unlocked: false)
let level3 = Level(id: 3, boss: "Chupacabra", unlocked: false)
let level4 = Level(id: 4, boss: "Yeti", unlocked: false)
  • Property observers(属性观察器)

struct Level {
        static var highestLevel = 1 // 类型属性
        let id: Int
        var boss: String
        var unlocked: Bool {
            didSet {
                if unlocked && id > Level.highestLevel {
                    Level.highestLevel = id
                }
            }
        }
}
现在,当玩家解锁一个新级别时,如果级别是新的高度,它将更新highestLevel类型属性。这里有几件事需要注意:
您可以从didSet观察器中访问未锁定的值。请记住,在设置了值之后,didSet就会被调用。
即使您是在类型的一个实例中,您仍然需要访问类型属性,它们的全名都是级别的。高水平的,而不是仅仅是高水平的。
另外,请记住,在初始化期间设置属性时,不会调用willSet和didSet观察器;只有当您为完全初始化的实例分配一个新值时,才会调用它们。这意味着属性观察器只对可变属性有用,因为常量属性只在初始化期间设置。
  • Limiting a variable(限制一个变量)

struct LightBulb {
      static let maxCurrent = 40
      var current = 0 {
          didSet {
              if current > LightBulb.maxCurrent {
                print("Current too high, falling back to previous setting.")
                current = oldValue
              }
          }
       }
}
 - ###测试:
var light = LightBulb()
light.current = 50
var current = light.current // 0
light.current = 40
current = light.current // 40
// 你试着把灯泡设置为50安培,但是灯泡拒绝了输入。很酷!
  • Lazy properties(懒加载属性

struct Circle {
      // 这里,您不相信从标准库中可以获得的pi值;您需要自己计算它
      lazy var pi = {
          return ((4.0 * atan(1.0 / 5.0)) - atan(1.0 / 239.0)) * 4.0
      }()
      var radius = 0.0
      var circumference: Double {
          mutating get {
              return pi * radius * 2
          }
      }
      init (radius: Double) {
          self.radius = radius
      }
}
//  您可以使用它的初始化器创建一个新的圆,而pi计算将不会运行:
var circle = Circle(radius: 5) // got a circle, pi has not been run
let circumference = circle.circumference // 31.42 // also, pi now has a value

在前一章中,我们学习了属性,它们是常量和变量,它们是结构的一部分。方法,正如您已经看到的,仅仅是驻留在结构中的函数。
在本章中,您将更深入地了解方法和初始化器。与属性一样,您将开始设计更复杂的结构。在本章中学习的内容将适用于所有命名类型的方法,包括类和枚举,您将在后面的章节中看到。

  • 八、第八部分:Methods

通过计算属性,您可以在最后一章看到,您可以从一个结构中运行代码。这听起来很像一种方法。有什么区别呢?这确实取决于风格,但有一些有用的想法可以帮助你做出决定。属性保存您可以得到和设置的值,而方法执行工作。有时,当方法的唯一目的是返回一个值时,这种区别就会变得模糊。
问问自己,你是否想要能够设定一个价值,并获得价值。计算属性可以在内部设置一个setter组件来编写值。另一个需要考虑的问题是,计算是否需要进行大量的计算或从数据库中读取数据。即使是一个简单的值,一个方法也可以帮助您向未来的开发人员指出,这个调用在时间和计算资源方面是昂贵的。如果调用很便宜,就使用计算属性。


  • Turning a function into a method(将一个函数转换为一个方法)

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)!
}
print(monthsUntilWinterBreak(from:SimpleDate(month: "May"))) // 7

如果把方法定义在Struct中会不会更容易理解呢

struct SimpleDate {
      var month: String
      func monthsUntilWinterBreak1(from date: SimpleDate) -> Int {
            return months.index(of: "December")! - months.index(of: date.month)!
      }
      // 推荐写法
       func monthsUntilWinterBreak() -> Int {
            return months.index(of: "December")! - months.index(of: self.month)!
        }
}
let date = SimpleDate(month: "October")
let monthsLeft = date.monthsUntilWinterBreak(from: date) // 2
  • mutating methods()

结构中的方法不能改变实例的值,而不被标记为“ mutating”。变异关键字标记了一个改变结构值的方法。由于一个结构是一个值类型,每次它在一个应用程序中传递时,系统就会复制它。如果一个方法改变了其中一个属性的值,那么原来的实例和复制的实例就不再是等价的了
通过将一个方法标记为mutating,您告诉快速编译器这个方法不应该在常量上调用。这就是Swift知道在编译时允许哪些方法和拒绝哪些方法。如果您在一个常量结构上调用一个mutating方法,编译器会将其标记为一个错误,在您运行程序之前必须纠正这个错误。

struct SimpleDate {
        var month: String
        var day: Int
        init(month: String, day: Int) {
            self.month = month
            self.day = day
        }
        mutating func advance() {
            day += 1 // 没有加mutating修饰func的话,会编译错误,不能在结构体提直接修改day的值
        }
}
var current = SimpleDate(month: "December", day: 30)
current.advance()
let currentMonth = current.month // December; should be January!
let currentDay = current.day // 31;
没有加mutating修饰func的话,会编译错误.png
  • Type methods(类方法)

与类型属性一样,您可以使用类型方法来访问所有实例中的数据。您在类型本身上调用类型方法,而不是在实例上调用。要定义类型方法,您需要使用静态修饰符来对其进行前缀。
类型方法对于一般情况下的类型是有用的,而不是特定的实例。

struct Math {
        // 1 类方法,关键字static修饰
        static func factorial(of number: Int) -> Int {
            // 2
            return (1...number).reduce(1, *)
        }
}
// 3
let factorial = Math.factorial(of: 6) // 720
  • extensions(扩展)

struct SimpleDate {
        var month: String
        var day: Int
 }
extension SimpleDate {
        init() {
            month = "March"
            day = 1
        }
}
let defaultDay = SimpleDate()
let childrensDay = SimpleDate(month: "May", day: 5)

您学习了结构体,它是一个命名类型,它允许您定义自己的类型。在本章中,您将熟悉,它们与结构非常相似,它们被命名为类型,具有属性,并且可以定义方法。
但是,您将学习的类是引用类型,而不是值类型,它们的功能和优点与它们的结构相比具有很大的不同。虽然您经常在应用程序中使用结构来表示值,但通常使用类来表示对象。

  • 九、第九部分:Classes

    • 结构体和类的区别

    • Reference types(引用类型)class
class Person {
      let name: String
      init(name: String) {
          self.name = name
      }
}
var var1 = Person(name: "John")

内存.png

如果您要创建一个新的变量var2并将其赋值为var1的值:
var var2 = var1
内存.png

  • value types(值类型)struct

struct Person {
      let name: String
}
var1 = Person(name: "John")

内存.png

如果您要创建一个新的变量var2并将其赋值为var1的值,然后,var1的值将被复制到var2:
var var2 = var1
内存.png

  • help(帮助理解)

当您创建诸如类之类的引用类型时,系统将实际的实例存储在称为堆的内存区域中。一个值类型的实例,例如一个结构体,驻留在一个名为堆栈的内存区域中,除非该值是类实例的一部分,在这种情况下,该值被存储在类实例的其余部分中。
堆和堆栈都在执行任何程序时都具有重要的作用。对它们是什么以及它们的工作原理有一个大致的了解,可以帮助你理解类和结构之间的功能差异:
该系统使用堆栈来存储执行的即时线程上的任何内容;它受到CPU的严格管理和优化。当函数创建一个变量时,堆栈会存储该变量,然后在函数退出时销毁它。因为这个堆栈组织得很好,所以它非常高效,而且非常快。


help.png

当您创建一个类的实例时,您的代码请求堆上的一个内存块来存储实例本身;这是图表右侧的实例的第一个名称和最后一个名称。它将该内存的地址存储在堆栈的命名变量中;这是存储在图左侧的引用。
当您创建一个struct时,值本身就存储在堆栈中,而堆永远不会涉及到。

  • 示例代码

值类型的工作

struct Location {
          var x: CGFloat
          var y: CGFloat
}
struct DeliveryArea {
          var range: Double
          let center: Location
}
var area1 = DeliveryArea(range: 2.5, center: Location(x: 2, y: 4))
var area2 = area1
print(area1.range) // 2.5
print(area2.range) // 2.5
area1.range = 4  
print(area1.range) // 4.0
print(area2.range) // 2.5 不会变为4.0

引用类型

class DeliveryArea {
        var range: Double
        let center: Location
}
var area1 = DeliveryArea(range: 2.5, center: Location(x: 2, y: 4))
var area2 = area1
print(area1.range) // 2.5
print(area2.range) // 2.5
area1.range = 4  
print(area1.range) // 4.0
print(area2.range) // 4.0 不会再是2.5 
  • Object identity(OC中的isEqual:)

在Swift中,===操作符让您检查一个对象的标识是否等于另一个对象的标识:



.png
  • Preventing inheritance防止继承(final关键字)

防止类继承

class Person {
    //...
}
final class Student: Person {
    //...
}
// Build error!
class StudentAthlete: Student {
    //...
}

防止方法重写

class Student: Person {
  final func recordGrade(_ grade: Grade) {
//...
} }
class StudentAthlete: Student {
        // Build error!
        override func recordGrade(_ grade: Grade) {
          //...
        } 
}
  • Required and convenience initializers

    • Required(必须实现)

class Student {
          let firstName: String
          let lastName: String
          required init(firstName: String, lastName: String) {
              self.firstName = firstName
              self.lastName = lastName
          }
    //... 创建Student对象必须实现 required init(firstName: String, lastName: String),否则编译错误
}
Student(firstName: "留", lastName: "明")
不实现报错.png
  • convenience(便利构造)

class Student {
          convenience init(transfer: Student) {
              self.init(firstName: transfer.firstName, lastName: transfer.lastName)
          }
}

十、第十部分:Enumerations

e.g:举个例子

enum Month {
  case january
  case february
  case march
  case april
  case may
  case june
  case july
  case august
  case september
  case october
  case november
  case december
}
func semester(for month: Month) -> String {
  switch month {
      case Month.august, Month.september, Month.october,
       Month.november, Month.december:
          return "Autumn"
      case Month.january, Month.february, Month.march, Month.april,
       Month.may:
          return "Spring"
    default:
          return "Not in the school year"
  }
}
  • Raw values

enum Month: Int {
          case january = 1, february = 2, march = 3, april = 4, may = 5,
  june = 6, july = 7, august = 8, september = 9,
  october = 10, november = 11, december = 12
}
// 第一个赋值即可,后面的赋值可以省略,自动+1
enum Month: Int {
          case january = 1, february, march, april, may, june, july,
  august, september, october, november, december
}
func monthsUntilWinterBreak(from month: Month) -> Int {
         return Month.december.rawValue - month.rawValue
}
monthsUntilWinterBreak(from: .april) // 8
  • Initializing with the raw value

 let fifthMonth = Month(rawValue: 5)
monthsUntilWinterBreak(from: fifthMonth) // Error: value not unwrapped,可选值,必须明确类型解包
monthsUntilWinterBreak(from: fifthMonth!) // Error: value not 
 let fifthMonth = Month(rawValue: 5)!
monthsUntilWinterBreak(from: fifthMonth)  // 7
  • String raw values

// 1
enum Icon: String {
        case music
        case sports
        case weather
        var filename: String {
            // 2
            return "\(rawValue.capitalized).png"
        }
}
let icon = Icon.weather
icon.filename // Weather.png
icon.rawValue // Weather
icon.hashValue // 2
  • Enumeration as state machine

交通灯

enum TrafficLight {
        case red, yellow, green
}
let trafficLight = TrafficLight.red


  • 轻轻点击,关注我简书

轻轻点击,关注我简书

轻轻点击,关注我微博

浏览我的GitHub


  • 扫一扫,关注我

扫一扫,关注我.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容