2015-02-06 65 views
12

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

在苹果文档,你可以阅读:

“扩展增加新的功能,以现有的类,结构或枚举类型”

那么,为什么不直接写我自己的代码里面我自己的班级?与我想扩展某些外部类的功能不同,如NSURLSessionDictionary,其中使用扩展名。

马特汤普森在他的阿拉莫菲尔图书馆使用扩展,也许他可以给我一点解释,他为什么选择这种方法。

+0

使用你喜欢的任何一个,但扩展使它比单纯的'MARK:'更明确,明确指出它开始的位置和结束位置。坦率地说,这不是一个问题,因为我会同时使用'MARK:'和'extension'。 'extension'的另一个优点是您可以轻松地折叠该代码(例如“编辑器” - “代码折叠...” - “折叠”或单击带阴影的左边距)。 – Rob 2015-11-19 22:03:55

回答

14

对我来说,这似乎是完全合理的,因为您可以使用扩展来将不同部分的逻辑公开给不同的扩展。这也可以用来制造类一致性协议更具有可读性,比如

class ViewController: UIViewController { 
... 
} 

extension ViewController: UITableViewDelegate { 
... 
} 

extension ViewController: UITableViewDataSource { 
... 
} 

extension ViewController: UITextFieldDelegate { 
... 
} 

协议方法是在清晰度不同的扩展分开,这似乎是更好的阅读比让说:

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

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

+0

什么NSCoding协议?您不能使用此协议的扩展名。 – Deny 2015-02-06 15:04:45

+0

这是棘手的地方,扩展不能添加指定的初始值设定项,因此NSCoding的'init(coder:)'不应该通过扩展来实现,不太确定我自己。 – libec 2015-02-07 00:53:41

+0

如果您需要访问您正在扩展的课程中的任何私人内容,则不起作用,是吗?或者我错过了什么? – 2016-11-06 10:55:25

6

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

如果没有扩展,想象声明你的类型为:使用扩展,在那里你捆绑在一起的协议的执行情况,实现它的这些具体方法

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来标记你的协议实现(并且记住,你可以结合使用这两种技术),但是你仍然会被分割到文件的顶部,在那里协议支持的声明将会是b e,以及文件的主体,你的实现在哪里。另外,请记住,如果您正在实施一个协议,您将随时获得来自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 
     } 
    } 
} 

因此,您必须在同一个扩展中实现两者。

+1

好的,所以当你的类源代码只有几页时,你就看不到这个类实现了哪个协议。您必须通过代码搜索这些信息。对我来说,这不比在代码中使用MARK更清晰。 – Deny 2015-02-06 15:00:24

+0

马的课程,但我会说使用语言而不是评论来表明意图更清晰。另请参阅我的编辑,它使您可以轻松实现协议。 – 2015-02-06 15:14:05

+0

感谢您的意见。我会尝试将两种方法结合使用 - 使用扩展名并将其标记为// MARK。 – Deny 2015-02-07 10:24:29