【问题标题】:Swift and using class extensionSwift 和使用类扩展
【发布时间】:2015-04-06 17:22:33
【问题描述】:

我不明白为什么程序员在他们的类实现中使用extension 关键字。您可以在其他主题中阅读代码在语义上更加分离等等。但是当我使用自己的代码时,使用// MARK - Something 对我来说感觉更清楚。那么当你在 Xcode 中使用方法列表 (ctrl+6) 时,第一眼就能看到一切。

在 Apple 文档中,您可以阅读:

“扩展为现有的类、结构或枚举类型添加了新功能。”

那么为什么不直接在我自己的类中编写我自己的代码呢?与我想扩展某些外部类的功能时不同,例如 NSURLSessionDictionary,您必须使用扩展。

Mattt Thompson 在他的 Alamofire 库中使用扩展,也许他可以给我一点解释,他为什么选择这种方法。

【问题讨论】:

  • 使用任何你喜欢的,但扩展使它比单纯的MARK: 更明确,清楚地指定它的开始和结束位置。坦率地说,这不是非此即彼的问题,因为我将同时使用MARK:extensionextension 的另一个优点是您可以轻松折叠该代码(例如“编辑器”-“代码折叠...”-“折叠”或单击阴影左边距)。

标签: class swift alamofire class-extensions


【解决方案1】:

使用扩展允许您将协议一致性声明保留在实现该协议的方法旁边。

如果没有扩展,想象一下将你的类型声明为:

struct Queue<T>: SequenceType, ArrayLiteralConvertible, Equatable, Printable, Deflectable, VariousOtherables {

// lotsa code...

// and here we find the implementation of ArrayLiteralConvertible
    /// Create an instance containing `elements`.
    init(arrayLiteral elements: T…) {
        etc
    } 

}

与使用扩展相比,您将协议的实现与实现它的特定方法捆绑在一起:

struct Queue<T> {
   // here go the basics of queue - the essential member variables,
   // maybe the enqueue and dequeue methods 
}

extension SequenceType {
    // here go just the specifics of what you need for a sequence type
    typealias Generator = GeneratorOf<T>
    func generate() -> Generator {
        return GeneratorOf { 
          // etc.
        }
    }
}

extension Queue: ArrayLiteralConvertible {
    init(arrayLiteral elements: T...) {
        // etc.
    }
}

是的,您可以使用// MARK 标记您的协议实现(请记住,您可以将这两种技术结合起来),但您仍然会被拆分到文件顶部,即协议支持声明所在的位置,以及文件的主体,您的实现所在的位置。

另外,请记住,如果您正在实施协议,您将在执行过程中从 IDE 获得有用的(如果稍微冗长的话)反馈,告诉您还有什么需要实施。使用扩展逐个完成每个协议(对我而言)比一次性完成(或在添加它们时从上到下来回跳跃)要容易得多。

鉴于此,将其他非协议但相关的方法也分组到扩展中是很自然的。

实际上,当您不能做到这一点时,我偶尔会感到沮丧。例如,

extension Queue: CollectionType {
    // amongst other things, subscript get:
    subscript(idx: Index) -> T {
        // etc
    }
}

// all MutableCollectionType adds is a subscript setter
extension Queue: MutableCollectionType {
    // this is not valid - you’re redeclaring subscript(Index)
    subscript(idx: Int) -> T {
        // and this is not valid - you must declare
        // a get when you declare a set
        set(val) {
            // etc
        }
    }
}

所以你必须在同一个扩展中实现两者。

【讨论】:

  • 好的,所以当您的类源代码只有几页时,您看不到该类实现了哪些协议。您必须通过代码搜索此信息。对我来说,这并不比在代码中使用 // MARK 更清楚。
  • 课程用马,但我想说使用语言而不是 cmets 来表示意图更清楚。另请参阅我的编辑,根据我的经验,它可以让您更轻松地实施协议。
  • 感谢您的意见。我将尝试结合这两种方法 - 使用扩展并用 // MARK 标记它们。
【解决方案2】:

对我来说这似乎是完全合理的,因为您可以使用扩展将不同的逻辑部分暴露给不同的扩展。这也可以用于使类符合协议更具可读性,例如

class ViewController: UIViewController {
...
}

extension ViewController: UITableViewDelegate {
...
}

extension ViewController: UITableViewDataSource {
...
}

extension ViewController: UITextFieldDelegate {
...
}

为了清楚起见,协议方法在不同的扩展中分开,这似乎比我们说的要好得多:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {}

所以,我想说使用扩展使您自己的代码更具可读性并没有什么坏处,而不仅仅是从 SDK 扩展现有的类。使用扩展可以避免在控制器中包含大量代码,并将功能拆分为易于阅读的部分,因此使用这些没有缺点。

【讨论】:

  • NSCoding 协议呢?您不能为此协议使用扩展。
  • 这就是棘手的地方,扩展不能添加指定的初始化器,因此 NSCoding 的 init(coder:) 不应该通过扩展实现,我自己不太确定。
  • 如果你需要从你扩展的类中访问任何私有的东西,它就不起作用了,是吗?还是我错过了什么?
  • 没错,这是 Swift 3 中的新功能。你需要使用 fileprivate 关键字来访问扩展名中的内容(如果你在同一个文件中有这些扩展名,显然)。在以前的 swift 版本中,private 意味着默认情况下文件是私有的。
猜你喜欢
  • 1970-01-01
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-27
相关资源
最近更新 更多