【问题标题】:Shouldn't every .swift file be a class?每个 .swift 文件不应该是一个类吗?
【发布时间】:2016-05-29 20:03:21
【问题描述】:

我在 ObjC 方面有一些经验,而且我刚刚开始学习 Swift。在 Objc 中,一切都是通过.h.m 中的@interface@implementation 的类,或者在我见过的其他Swift 类中,一切通常都是某种形式的

class MyCustomClassInhertingfrom: SomeFoundationClass { //methods & properties}

然而在一些名为pancakeHouse.Swift 的类中却没有提到关键字class 为什么?这不是模型类吗?这不会破坏 MVC 设计模式吗?发生这种情况是因为 Swift 中枚举和结构与类的新强大功能吗?_____我显然很困惑!

import UIKit
import CoreLocation

enum PriceGuide : Int {
  case Unknown = 0
  case Low = 1
  case Medium = 2
  case High = 3
}

extension PriceGuide : CustomStringConvertible {
  var description : String {
    switch self {
    case .Unknown:
      return "?"
    case .Low:
      return "$"
    case .Medium:
      return "$$"
    case .High:
      return "$$$"
    }
  }
}

enum PancakeRating {
  case Unknown
  case Rating(Int)
}

extension PancakeRating {
  init?(value: Int) {
    if value > 0 && value <= 5 {
      self = .Rating(value)
    } else {
      self = .Unknown
    }
  }
}

extension PancakeRating {
  var ratingImage : UIImage? {
    guard let baseName = ratingImageName else {
      return nil
    }
    return UIImage(named: baseName)
  }

  var smallRatingImage : UIImage? {
    guard let baseName = ratingImageName else {
      return nil
    }
    return UIImage(named: "\(baseName)_small")
  }

  private var ratingImageName : String? {
    switch self {
    case .Unknown:
      return nil
    case .Rating(let value):
      return "pancake_rate_\(value)"
    }
  }
}



 struct PancakeHouse {
  let name: String
  let photo: UIImage?
  let thumbnail: UIImage?
  let priceGuide: PriceGuide
  let location: CLLocationCoordinate2D?
  let details: String
  let rating: PancakeRating
}

extension PancakeHouse {
   init?(dict: [String : AnyObject]) {
    guard let name = dict["name"] as? String,
      let priceGuideRaw = dict["priceGuide"] as? Int,
      let priceGuide = PriceGuide(rawValue: priceGuideRaw),
      let details = dict["details"] as? String,
      let ratingRaw = dict["rating"] as? Int,
      let rating = PancakeRating(value: ratingRaw) else {
        return nil
    }

    self.name = name
    self.priceGuide = priceGuide
    self.details = details
    self.rating = rating

    if let imageName = dict["imageName"] as? String where !imageName.isEmpty {
      photo = UIImage(named: imageName)
    } else {
      photo = nil
    }

    if let thumbnailName = dict["thumbnailName"] as? String where !thumbnailName.isEmpty {
      thumbnail = UIImage(named: thumbnailName)
    } else {
      thumbnail = nil
    }

    if let latitude = dict["latitude"] as? Double,
      let longitude = dict["longitude"] as? Double {
        location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
    } else {
      location = nil
    }
  }
}

extension PancakeHouse {
  static func loadDefaultPancakeHouses() -> [PancakeHouse]? {
    return self.loadPancakeHousesFromPlistNamed("pancake_houses")
  } 

  static func loadPancakeHousesFromPlistNamed(plistName: String) -> [PancakeHouse]? {
    guard let path = NSBundle.mainBundle().pathForResource(plistName, ofType: "plist"),
      let array = NSArray(contentsOfFile: path) as? [[String : AnyObject]] else {
        return nil
    }

    return array.map { PancakeHouse(dict: $0) }
                .filter { $0 != nil }
                .map { $0! }
  }
}

extension PancakeHouse : CustomStringConvertible {
  var description : String {
    return "\(name) :: \(details)"
  }
}

extension PancakeHouse: Equatable {
}

func ==(lhs: PancakeHouse, rhs: PancakeHouse) -> Bool {
  return lhs.name == rhs.name
}

注意:我希望得到一个答案,其中还包括 .swift.h + .m 的比较,即不要只将其视为特定问题,将其视为一般问题并解释或链接理解此问题所需的先决条件的详细信息)

【问题讨论】:

  • MVC 和 OOP 没有共同点。您可以使用 OOP(对象、类)来实现 MVC 模式,但您不必这样做。此外,从架构的角度来看,类和结构几乎是相同的。您可以使用它们中的任何一个来实现一个对象。
  • 头文件 (*.h) 存在的原因纯粹是历史原因。使用它们是因为 40 年前的编译器需要它们,而我们现在无法将它们从语言中删除。 .swift 文件是 .m 文件,你不需要 .h
  • 有这个WWDC2015 video,涉及到一些主题,也许有助于进一步理解这个主题。

标签: objective-c swift class model-view-controller


【解决方案1】:

pancakeHouse.swift 定义了PancakeHouse 以及与之相关的所有各种东西。这是非常好的 Swift 风格。这里没有 class,因为 PancakeHouse 恰好是 struct,这也是非常好的 Swift 风格(并且略微偏爱)。结构很像 Swift 中的类,因为它们可以有数据和方法(和扩展)。

ObjC 不要求每个类都定义在自己的 .h/.m 对中,但这样做是相当典型的 ObjC 风格。也就是说,即使在 ObjC 中也有例外。通常在与基类相同的文件中定义可变子类(NSArrayNSMutableArray 都在NSArray.h 中定义)。 Swift 风格已经演变为将相关的事物更紧密地集中在一起。如果您知道其名称,则一种样式可以更轻松地找到某些东西。另一个更容易一起找到相关的概念。两者都有自己的优势,一旦人们习惯了一种,他们往往会相信那一种显然是正确的。但它们只是组织方式不同。

请注意,此文件还使用 Swift 扩展来分解相关方法。这也是常见且良好的 Swift。旧的 ObjC 使用类别来做到这一点,但如今以这种方式组织 ObjC 代码的情况要少得多(类别现在用于其他事情)。同样,两者都不是完全正确的。只是样式已经演变。

【讨论】:

  • 我明白了.... 必须 swift 文件的命名与struct PancakeHouse 匹配吗?我的意思是它也在其中定义了另外两个枚举。它不能匹配他们的任何名字吗?对我来说,swift 文件似乎只是一个没有附加语义含义的包装器。对吗?
  • 正确。它可能被命名为 pancake.swift。我有一个名为 Logging.swift 的文件,它定义了一个 Log 结构和其他与日志记录相关的内容。系统中的任何内容实际上都称为“日志记录”。也就是说,在 Cocoa 编程中,通常会将您的 View Controller 放入共享名称的文件中。在大多数情况下,您应该倾向于以主要类型命名文件。但是次要类型往往会与其主要类型聚集在一起。这就是 Swift 迄今为止的发展方式(在 Swift 中次要类型比 ObjC 更常见)。
  • 很好的总结。切线跟进“轻度首选的结构”的事情:小心。值类型很棒,但是因为它们是 Swift 的新亮点之一,所以它们往往会被过度使用。在这种情况下,如果您开始制作用户可编辑的评分,您可能会遇到麻烦。 (部分还取决于您如何构建应用程序的其余部分。)另外......嗯,煎饼。
  • 我刚刚遇到了一个小问题:我在不同的 swift 文件中有 2 个名称相同的函数。例如我在文件 x 和文件 y 中写入了 func abc()。这混淆了我的调试心态。我以为我正在从文件 y 调试 func abc,但我在文件 x 上。出于这个原因,我认为将不同的类放在具有类名的文件中可能会更好。 (尽管仍然如您所说,将可变类保留在同一个文件中)
【解决方案2】:

Swift 文件只需要有效的 swift 代码。就这样。在这种情况下,您的 swift 文件只是定义了两个枚举。一般来说,最好将每个枚举(及其扩展名)放在它自己的 .swift 文件中,但这只是你的个人意见。

A .m 是 .h 接口的实现。 !@#$ 让这两件事始终保持最新是一件很痛苦的事情。 Swift 只需将它们合并到一个事物/文件中,就可以轻松得多,并且您的整个项目会根据您设置的访问权限自动看到它。如果您没有设置任何访问权限,如上面的示例,那么您将拥有“内部”,这意味着整个项目。

【讨论】:

  • 忘记了结构定义......哦......我明白了。所以理论上这更像是组合的捆绑 .swift 文件,它具有enum PriceGuidePancakeRatingstruct PancakeHouse 的定义。在 ObjC 中,如果我们想这样做,我们必须将它们放在 .h.m 文件中,这最终会将其封装在一个类中,对吧?
  • 如果您发现保持 .h 和 .m 文件同步很痛苦,那么您可能在 .h 文件中放入了太多内容。如果你做得对的话,这两个文件中提到的东西很少。
  • 两者都提到了任何公开的内容。如果您更改公共方法的签名,则必须在两个地方编辑相同的内容。这就是为什么 .h 文件总是很痛苦的原因。
【解决方案3】:

编译器作者可以对这个问题采取两种哲学:

  • 每个公共类都需要一个具有匹配名称的文件 - 这是 Java 的做法。尽管它的限制很小,但其背后的逻辑是让编译器无需查看所有文件即可找到类引用。它还可以帮助程序员组织他们的代码。
  • 做你自己的方式 - 这是 Swift 和 C、C#、Objective-C 一起走的路。从本质上讲,编译器编写者会告诉您,无论您将类放在哪里,他们的编译器都会找到您的类,让您以一种对您和您的团队来说最直观的方式组织代码。

【讨论】:

  • 哦。我开始质疑我对结构和枚举的全部理解...... AFAIK 与 Swift 相反,一切都是自己的——不需要类......在 ObjC 中,所有的可见性或访问权限都由一个类管理,这意味着如果你想要使用一个结构,你必须导入它的类.h 文件。对吗?
  • @asma22 Obj-C 类也有公共数据成员,如果你想可以使用它们(这不是通常是个好主意,但那是一个不同的故事)。另一方面,结构是 C 动物,访问控制由包含或不包含头文件代替。
  • 我刚刚遇到了一个小问题:我在不同的 swift 文件中有 2 个名称相同的函数。例如我在文件 x 和文件 y 中写入了 func abc()。这混淆了我的调试心态。我以为我正在从文件 y 调试 func abc,但我在文件 x 上。出于这个原因,我认为将不同的类放在同名的文件中可能会更好。 (尽管仍然如 Rob 所说,将可变类保留在同一个文件中或将任何可以独立具有句法含义的东西分开)
猜你喜欢
  • 2020-09-09
  • 1970-01-01
  • 2017-08-16
  • 1970-01-01
  • 2019-09-25
  • 2011-12-24
  • 2010-09-26
  • 2011-03-02
相关资源
最近更新 更多