How to properly use class extensions in Swift?

后端 未结 3 1873
野性不改
野性不改 2020-12-09 04:20

In Swift, I have historically used extensions to extend closed types and provide handy, logic-less functionality, like animations, math extensions etc. However, since extens

3条回答
  •  情书的邮戳
    2020-12-09 04:33

    I use a similar approach, which can be described in one sentence:

    Sort a type's responsibilities into extensions

    These are examples for aspects I'm putting into individual extensions:

    • A type's main interface, as seen from a client.
    • Protocol conformances (i.e. a delegate protocol, often private).
    • Serialization (for example everything NSCoding related).
    • Parts of a types that live on a background thread, like network callbacks.

    Sometimes, when the complexity of a single aspect rises, I even split a type's implementation over more than one file.

    Here are some details that describe how I sort implementation related code:

    • The focus is on functional membership.
    • Keep public and private implementations close, but separated.
    • Don't split between var and func.
    • Keep all aspects of a functionality's implementation together: nested types, initializers, protocol conformances, etc.

    Advantage

    The main reason to separate aspects of a type is to make it easier to read and understand.

    When reading foreign (or my own old) code, understanding the big picture is often the most difficult part of diving in. Giving a developer an idea of a context of some method helps a lot.

    There's another benefit: Access control makes it easier not to call something inadvertently. A method that is only supposed to be called from a background thread can be declared private in the "background" extension. Now it simply can't be called from elsewhere.

    Current Restrictions

    Swift 3 imposes certain restrictions on this style. There are a couple of things that can only live in the main type's implementation:

    • stored properties
    • overriding func/var
    • overidable func/var
    • required (designated) initializers

    These restrictions (at least the first three) come from the necessity to know the object's data layout (and witness table for pure Swift) in advance. Extensions can potentially be loaded late during runtime (via frameworks, plugins, dlopen, ...) and changing the type's layout after instances have been created would brake their ABI.

    A modest proposal for the Swift team :)

    All code from one module is guaranteed to be available at the same time. The restrictions that prevent fully separating functional aspects could be circumvented if the Swift compiler would allow to "compose" types within a single module. With composing types I mean that the compiler would collect all declarations that define a type's layout from all files within a module. Like with other aspects of the language it would find intra file dependencies automatically.

    This would allow to really write "aspect oriented" extensions. Not having to declare stored properties or overrides in the main declaration would enable better access control and separation of concerns.

提交回复
热议问题