Swift 2.0 Advanced Summary

Chapters: Enumerations - Advanced Operators

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2 Prerelease).” iBooks. https://itun.es/us/k5SW7.l

Methods

  • Classification of methods.

    • Instance methods are functions that belong to instances of a particular class, structure, or enumeration. They support the functionality of those instances, either by providing ways to access and modify instance properties, or by providing functionality related to the instance’s purpose.

    • Type methods are methods that are called on the type itself. You indicate type methods by writing the keyword static before the method’s func keyword. Classes may also use the class keyword to allow subclasses to override the superclass’s implementation of that method.

  • How to modify value types from within instance types?

    Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.

    However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.

    Note that you cannot call a mutating method on a constant of structure type, because its properties cannot be changed, even if they are variable properties

    struct Point {
      var x = 0.0, y = 0.0
      mutating func moveByX(deltaX:Double, y deltaY:Double) {
        x += deltaX
        y += deltaY
      }
      mutating func moveToX(newX:Double, y newY:Double) {
        self = Point(x:newX, y:newY)
      }
    }
    
    enum TriStateSwitch {
      case Off, Low, High
      mutating func next() {
        switch self {
          case Off:
            self = Low
          case Low:
            self = High
          case High:
            self = Off
        }
      }
    }
    

Subscripts

  • What is subscript syntax?

    Subscripts enable you to query instances of a type by writing one or more values in square brackets after the instance name. You write subscript definitions with the subscript keyword, and specify one or more input parameters and a return type, in the same way as instance methods. Unlike instance methods, subscripts can be read-write or read-only. This behavior is communicated by a getter and setter in the same way as for computed properties

    subscript(index:Int) -> Int {
      get {
        // return an appropriate subscript value here
      }
      set(newValue) {
        // perform a suitable setting action here
      }
    }
    
  • subscript options

    Subscripts can take any number of input parameters, and these input parameters can be of any type. Subscripts can also return any type. Subscripts can use variable parameters and variadic parameters, but cannot use in-out parameters or provide default parameter values.

    A class or structure can provide as many subscript implementations as it needs, and the appropriate subscript to be used will be inferred based on the types of the value or values that are contained within the subscript braces at the point that the subscript is used. This definition of multiple subscripts is known as subscript overloading.

    struct Matrix {
      subscript(row:Int, column:Int) -> Double {
        // ...
      }
    }
    let matrix = Matrix( *arguments* )
    matrix[0, 0] = 3.2
    

Inheritance

  • What is inheritance?

    A class can inherit methods, properties, and other characteristics from another class. When one class inherits from another, the inheriting class is known as a subclass, and the class it inherits from is known as its superclass. Any class that does not inherit from another class is known as a base class. Inheritance is a fundamental behavior that differentiates classes from other types in Swift.

  • How to prevent overrides?

    You can prevent a method, property, or subscript from being overridden by marking it as final. Do this by writing the final modifier before the method, property, or subscript’s introducer keyword (such as final var, final func, final class func, and final subscript).

    You can mark an entire class as final by writing the final modifier before the class keyword in its class definition (final class). Any attempt to subclass a final class is reported as a compile-time error.

Initialization

  • What is difference between Swift initializers and Objective-C initializers?

    Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.

    Instances of class types can also implement a deinitializer, which performs any custom cleanup just before an instance of that class is deallocated.

  • How to set initial values for stored properties?

    You can set an initial value for a stored property within an initializer, or by assigning a default property value as part of the property’s definition.

    Note that when you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.

    You can assign a value to a constant property at any point during initialization, as long as it is set to a definite value by the time initialization finishes. Once a constant property is assigned a value, it can’t be further modified.

  • What are memberwise initializers?

    Structure types automatically receive a memberwise initializer if they do not define any of their own custom initializers. Unlike a default initializer, the structure receives a memberwise initializer even if it has stored properties that do not have default values.

    struct Size {
      var width = 0.0, height = 0.0
    }
    let twoByTwo = Size(width: 2.0, height: 2.0)
    
  • What is initializer delegation?

    Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.

    For value types, you use self.init to refer to other initializers from the same value type when writing your own custom initializers. You can only call self.init from within an initializer.

  • What are designated initializers and convenience initializers?

    Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.

    Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.

    • Designated initializers must always delegate up.

    • Convenience initializers must always delegate across.

      // Designated initializers
      init (*parameters*) {
        *statements*
      }
      // Convenience initializers
      convenience init (*parameters*) {
        *statements*
      }
      
  • What is two-phase initialization?

    Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.

    The use of a two-phase initialization process makes initialization safe, while still giving complete flexibility to each class in a class hierarchy. Two-phase initialization prevents property values from being accessed before they are initialized, and prevents property values from being set to a different value by another initializer unexpectedly.

  • How does initializer inheritance work?

    Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.

    Note that superclass initializers are inherited in certain circumstances, but only when it is safe and appropriate to do so.

  • When to write override modifier?

    When you write a subclass initializer that matches a superclass designated initializer, you are effectively providing an override of that designated initializer. Therefore, you must write the override modifier before the subclass’s initializer definition. This is true even if you are overriding an automatically provided default initializer.

    Conversely, if you write a subclass initializer that matches a superclass convenience initializer, that superclass convenience initializer can never be called directly by your subclass. Therefore, your subclass is not (strictly speaking) providing an override of the superclass initializer. As a result, you do not write the override modifier when providing a matching implementation of a superclass convenience initializer.

  • When does Automatic initializer Inheritance happen?

    Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:

    • Rule 1

      If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

    • Rule 2

      If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

  • How to define failable initializers?

    class Animal {
      let species:String
      init?(species:String) {
        self.species = species
        if species.isEmpty {
          return nil
        }
      }
    }
    

    A failable initializer for a value type (that is, a structure or enumeration) can trigger an initialization failure at any point within its initializer implementation. For classes, however, a failable initializer can trigger an initialization failure only after all stored properties introduced by that class have been set to an initial value and any initializer delegation has taken place.

    Alternatively, you can define a failable initializer that creates an implicitly unwrapped optional instance of the appropriate type. Do this by placing an exclamation mark after the init keyword (init!) instead of a question mark.

    You can delegate from init? to init! and vice versa, and you can override init? with init! and vice versa. You can also delegate from init to init!, although doing so will trigger an assertion if the init! initializer causes initialization to fail.

  • What are required initializers?

    Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer.

  • How to set a default property value with a closure or function?

    class SomeClass {
      let someProperty: SomeType = {
        // create a default value for someProperty inside this closure
        // someValue must be of the same type as SomeType
        return someValue
      }()
    }
    

De-initialization

  • A deinitializer is called immediately before a class instance is deallocated. You write deinitializers with the deinit keyword, similar to how initializers are written with the init keyword. Deinitializers are only available on class types.

Automatic Reference Counting

  • What is the difference between weak and unowned references?

    Because a weak reference does not keep a strong hold on the instance it refers to, it is possible for that instance to be deallocated while the weak reference is still referring to it. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. You can check for the existence of a value in the weak reference, just like any other optional value, and you will never end up with a reference to an invalid instance that no longer exists.

    Because an unowned reference is non-optional, you don’t need to unwrap the unowned reference each time it is used. An unowned reference can always be accessed directly. However, ARC cannot set the reference to nil when the instance it refers to is deallocated, because variables of a non-optional type cannot be set to nil.

  • How to resolve strong reference cycles between classes?

    Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization.

  • How to resolve strong reference cycles for closures?

    You resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure’s definition. A capture list defines the rules to use when capturing one or more reference types within the closure’s body. As with strong reference cycles between two class instances, you declare each captured reference to be a weak or unowned reference rather than a strong reference. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code.

    lazy var someClosure: (Int, String) -> String = {
      [unowned self, weak delegate = self.delegate!] (index:Int, stringToProcess: String) -> String in
      // closure body goes here
    }
    

    Define a capture in a closure as an unowned reference when the closure and the instance it captures will always refer to each other, and will always be deallocated at the same time.

    Conversely, define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated. This enables you to check for their existence within the closure’s body.

Optional Chaining

  • How does optional chaining work?

    Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

  • Where to place the question mark?

    When you access a subscript on an optional value through optional chaining, you place the question mark before the subscript’s braces, not after. If a subscript returns a value of optional type — such as the key subscript of Swift’s Dictionary type — place a question mark after the subscript’s closing bracket to chain on its optional return value. The optional chaining question mark always follows immediately after the part of the expression that is optional.

Error Handling

  • How to represent errors?

    In Swift, errors are represented by values of types conforming to the ErrorType protocol.

  • How to throw errors?

    To indicate that a function or method can throw an error, you write the throws keyword in its declaration, after its parameters. If it specifies a return type, you write the throws keyword before the return arrow (->). A function, method, or closure cannot throw an error unless explicitly indicated.

    At any point in the body of a throwing function, you can throw an error with a throw statement.

    When you call a throwing function, you write try in front of the call. This keyword calls out the fact that the function can throw an error and that the lines of code after the try might not be run.

  • How to catch and handle errors?

    do {
      try *function that throws*
      *statements*
    } catch *pattern* {
      *statements*
    }
    

    If an error is thrown, that error is propagated to its outer scope until it is handled by a catch clause. A catch clause consists of the catch keyword, followed by a pattern to match the error against and a set of statements to execute.

    Like a switch statement, the compiler attempts to infer whether catch clauses are exhaustive. If such a determination can be made, the error is considered handled. Otherwise, the containing scope must handle the error, or the containing function must be declared with throws. To ensure that an error is handled, use a catch clause with a pattern that matches all errors. If a catch clause does not specify a pattern, the clause will match and bind any error to a local constant named error.

  • How to disable error propagation?

    Calling a throwing function or method with try! disables error propagation and wraps the call in a run-time assertion that no error will be thrown. If an error actually is thrown, you’ll get a runtime error.

  • How to specify clean-up actions?

    You use a defer statement to execute a set of statements just before code execution leaves the current block of code. This lets you do any necessary cleanup that should be performed regardless of whether an error occurred.

    A defer statement defers execution until the current scope is exited. It consists of the defer keyword and the statements to be executed later. The deferred statements may not contain any code that would transfer control out of the statements, such as a break or a return statement, or by throwing an error. Deferred actions are executed in reverse order of how they are specified—that is, the code in the first defer statement executes after code in the second, and so on.

Type Casting

  • What is type casting?

    Type casting is a way to check the type of an instance, and/or to treat that instance as if it is a different superclass or subclass from somewhere else in its own class hierarchy.

    Type casting in Swift is implemented with the is and as operators. These two operators provide a simple and expressive way to check the type of a value or cast a value to a different type.

    You can also use type casting to check whether a type conforms to a protocol

  • How to check type?

    Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.

  • What's the difference between conditional and force form of the type cast operator?

    A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (as? or as!).

    Because downcasting can fail, the type cast operator comes in two different forms. The conditional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as!, attempts the downcast and force-unwraps the result as a single compound action.

    Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.

    Use the forced form of the type cast operator (as!) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.

  • What is Any and AnyObject?

    • AnyObject can represent an instance of any class type.
    • Any can represent an instance of any type at all, including function types.

Nested Types

  • What are nested types?

    Swift enables you to define nested types, whereby you nest supporting enumerations, classes, and structures within the definition of the type they support. To nest a type within another type, write its definition within the outer braces of the type it supports. Types can be nested to as many levels as are required.

Extensions

  • What are extensions?

    Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C. (Unlike Objective-C categories, Swift extensions do not have names.) In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of.

    Extensions in Swift can:

    • Add computed properties and computed type properties
    • Define instance methods and type methods
    • Provide new initializers
    • Define and use new nested types
    • Make an existing type conform to a protocol
  • Extension syntax.

    extension someType {...}
    extension someType: SomeProtocol, AnotherProtocol {...}
    

Protocols

  • What is a protocol?

    A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

  • What is protocol syntax?

    protocol SomeProtocol {
      // property definitions
      var mustBeSettable:Int {get set}
      var doesNoteNeedToBeSettable: Int {get}
      static var someTypeProperty: Int {get set}
      // method definitions
      static func someTypeMethod()
      mutating func toggle()
      init(someParameter:Int)
    }
    struct SomeStructure: FirstProtocol, SecondProtocol {
      // Definitions
    }
    class SomeClass: SomeSuperClass, FirstProtocol, SecondProtocol {
      // Definitions
    }
    // Adding protocol conformation with an extension
    extension SomeClass: SomeProtocol {
      ...
    }
    // Declaring protocol adoption with an extension
    extension SomeClass: SomeProtocol {}
    // Protocol inheritance, Limit protocol adoption to class types
    protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
      ...
    }  
    
  • What is protocol composition?

    It can be useful to require a type to conform to multiple protocols at once. You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions have the form protocol<SomeProtocol, AnotherProtocol>. You can list as many protocols within the pair of angle brackets (<>) as you need, separated by commas.

  • How to check for protocol conformance?

    • The is operator returns true if an instance conforms to a protocol and returns false if it does not.
    • The as? version of the downcast operator returns an optional value of the protocol’s type, and this value is nil if the instance does not conform to that protocol.
    • The as! version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast does not succeed.
  • How to define optional protocol requirements?

    You can define optional requirements for protocols, These requirements do not have to be implemented by types that conform to the protocol. Optional requirements are prefixed by the optional modifier as part of the protocol’s definition.

    Optional protocol requirements can only be specified if your protocol is marked with the @objc attribute.

    Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. If you mark your protocol as @objc in order to specify optional requirements, you will only be able to apply that protocol to class types.

  • What are protocol extensions?

    Protocols can be extended to provide method and property implementations to conforming types. This allows you to define behavior on protocols themselves, rather than in each type’s individual conformance or in a global function.

    You can use protocol extensions to provide a default implementation to any method or property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension.

  • How to add constraints to protocol extensions?

    When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending using a where clause.

Generics

  • What is generics?

    Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.

    Generics are one of the most powerful features of Swift, and much of the Swift standard library is built with generic code. For example, Swift’s Array and Dictionary types are both generic collections.

    // **Generic functions** can work with any type.
    // func swapTwoInts(inout a: Int, inout _ b: Int)
    // func swapTwoValues<T>(inout a: T, inout _ b: T)
    // **Generic types** are custom classes, structures, or enums that can work with any type.
    // struct Stack<T> {
      var items = [T]()
    }
    // Type Constraint Syntax
    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
      // function body goes here
    }
    
  • What is a type parameter?

    In the swapTwoValues example above, the placeholder type T is an example of a type parameter. Type parameters specify and name a placeholder type, and are written immediately after the function’s name, between a pair of matching angle brackets (such as <T>).

    You can provide more than one type parameter by writing multiple type parameter names within the angle brackets, separated by commas.

  • What is associated type?

    When defining a protocol, it is sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name (or alias) to a type that is used as part of the protocol. The actual type to use for that associated type is not specified until the protocol is adopted. Associated types are specified with the typealias keyword.

  • What are where clauses?

    A where clause enables you to require that an associated type conforms to a certain protocol, and/or that certain type parameters and associated types be the same. You write a where clause by placing the where keyword immediately after the list of type parameters, followed by one or more constraints for associated types, and/or one or more equality relationships between types and associated types.

Access Control

  • Introduce terminologies.

    Access control restricts access to parts of your code from code in other source files and modules. This feature enables you to hide the implementation details of your code, and to specify a preferred interface through which that code can be accessed and used.

    A module is a single unit of code distribution—a framework or application that is built and shipped as a single unit and that can be imported by another module with Swift’s import keyword. Each build target (such as an app bundle or framework) in Xcode is treated as a separate module in Swift. If you group together aspects of your app’s code as a stand-alone framework—perhaps to encapsulate and reuse that code across multiple applications—then everything you define within that framework will be part of a separate module when it is imported and used within an app, or when it is used within another framework.

    A source file is a single Swift source code file within a module (in effect, a single file within an app or framework). Although it is common to define individual types in separate source files, a single source file can contain definitions for multiple types, functions, and so on.

    Swift provides three different access levels for entities within your code. These access levels are relative to the source file in which an entity is defined, and also relative to the module that source file belongs to.

    • Public access enables entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use public access when specifying the public interface to a framework. Example is public class SomePublicClass{}.

    • Internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure. Unless otherwise specified, the default access level is internal. Example is internal class SomeInternalClass{}.

    • Private access restricts the use of an entity to its own defining source file. Use private access to hide the implementation details of a specific piece of functionality. Example is private class SomePrivateClass{}.

  • What are access levels for different entities?

    • Custom Types

      If you want to specify an explicit access level for a custom type, do so at the point that you define the type. The new type can then be used wherever its access level permits. For example, if you define a private class, that class can only be used as the type of a property, or as a function parameter or return type, in the source file in which the private class is defined.

      The access control level of a type also affects the default access level of that type’s members (its properties, methods, initializers, and subscripts). If you define a type’s access level as private, the default access level of its members will also be private. If you define a type’s access level as internal or public (or use the default access level of internal without specifying an access level explicitly), the default access level of the type’s members will be internal.

    • Tuple Types

      The access level for a tuple type is the most restrictive access level of all types used in that tuple. For example, if you compose a tuple from two different types, one with internal access and one with private access, the access level for that compound tuple type will be private.

    • Function Types

      The access level for a function type is calculated as the most restrictive access level of the function’s parameter types and return type. You must specify the access level explicitly as part of the function’s definition if the function’s calculated access level does not match the contextual default.

    • Enumeration Types

      The individual cases of an enumeration automatically receive the same access level as the enumeration they belong to. You cannot specify a different access level for individual enumeration cases.

    • Raw Values and Associated Values

      The types used for any raw values or associated values in an enumeration definition must have an access level at least as high as the enumeration’s access level. You cannot use a private type as the raw-value type of an enumeration with an internal access level, for example.

    • Nested Types

      Nested types defined within a private type have an automatic access level of private. Nested types defined within a public type or an internal type have an automatic access level of internal. If you want a nested type within a public type to be publicly available, you must explicitly declare the nested type as public.

    • Subclassing

      You can subclass any class that can be accessed in the current access context. A subclass cannot have a higher access level than its superclass—for example, you cannot write a public subclass of an internal superclass.

      In addition, you can override any class member (method, property, initializer, or subscript) that is visible in a certain access context. An override can make an inherited class member more accessible than its superclass version.

    • Constants, Variables, Properties, and Subscripts

      A constant, variable, or property cannot be more public than its type. It is not valid to write a public property with a private type, for example. Similarly, a subscript cannot be more public than either its index type or return type.

      If a constant, variable, property, or subscript makes use of a private type, the constant, variable, property, or subscript must also be marked as private.

    • Getters and Setters

      Getters and setters for constants, variables, properties, and subscripts automatically receive the same access level as the constant, variable, property, or subscript they belong to.

      You can give a setter a lower access level than its corresponding getter, to restrict the read-write scope of that variable, property, or subscript. You assign a lower access level by writing private(set) or internal(set) before the var or subscript introducer.

    • Initializers

      Custom initializers can be assigned an access level less than or equal to the type that they initialize. The only exception is for required initializers (as defined in Required Initializers). A required initializer must have the same access level as the class it belongs to.

      As with function and method parameters, the types of an initializer’s parameters cannot be more private than the initializer’s own access level.

    • Default Initializers

      As described in Default Initializers, Swift automatically provides a default initializer without any arguments for any structure or base class that provides default values for all of its properties and does not provide at least one initializer itself.

      A default initializer has the same access level as the type it initializes, unless that type is defined as public. For a type that is defined as public, the default initializer is considered internal. If you want a public type to be initializable with a no-argument initializer when used in another module, you must explicitly provide a public no-argument initializer yourself as part of the type’s definition.

    • Default Memberwise Initializers for Structure Types

      The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Otherwise, the initializer has an access level of internal.

      As with the default initializer above, if you want a public structure type to be initializable with a memberwise initializer when used in another module, you must provide a public memberwise initializer yourself as part of the type’s definition.

    • Protocols

      If you want to assign an explicit access level to a protocol type, do so at the point that you define the protocol. This enables you to create protocols that can only be adopted within a certain access context.

      The access level of each requirement within a protocol definition is automatically set to the same access level as the protocol. You cannot set a protocol requirement to a different access level than the protocol it supports. This ensures that all of the protocol’s requirements will be visible on any type that adopts the protocol.

      • Protocol Inheritance

        If you define a new protocol that inherits from an existing protocol, the new protocol can have at most the same access level as the protocol it inherits from. You cannot write a public protocol that inherits from an internal protocol, for example.

      • Protocol Conformance

        A type can conform to a protocol with a lower access level than the type itself. The context in which a type conforms to a particular protocol is the minimum of the type’s access level and the protocol’s access level. If a type is public, but a protocol it conforms to is internal, the type’s conformance to that protocol is also internal.

    • Extensions

      You can extend a class, structure, or enumeration in any access context in which the class, structure, or enumeration is available. Any type members added in an extension have the same default access level as type members declared in the original type being extended.

      • Adding Protocol Conformance with an Extension

        You cannot provide an explicit access level modifier for an extension if you are using that extension to add protocol conformance. Instead, the protocol’s own access level is used to provide the default access level for each protocol requirement implementation within the extension.

    • Generics

      The access level for a generic type or generic function is the minimum of the access level of the generic type or function itself and the access level of any type constraints on its type parameters.

    • Type Aliases

      Any type aliases you define are treated as distinct types for the purposes of access control. A type alias can have an access level less than or equal to the access level of the type it aliases.

Advanced Operators

  • How to opt in to overflow behavior?

    Unlike arithmetic operators in C, arithmetic operators in Swift do not overflow by default. Overflow behavior is trapped and reported as an error. To opt in to overflow behavior, use Swift’s second set of arithmetic operators that overflow by default, such as the overflow addition operator (&+). All of these overflow operators begin with an ampersand (&).

  • Precedence and Associativity

    Operator precedence gives some operators higher priority than others; these operators are applied first.

    Operator associativity defines how operators of the same precedence are grouped together (or associated)—either grouped from the left, or grouped from the right. Think of it as meaning “they associate with the expression to their left,” or “they associate with the expression to their right.”
    “Precedence and Associativity
    Operator precedence gives some operators higher priority than others; these operators are applied first.

    Operator associativity defines how operators of the same precedence are grouped together (or associated)—either grouped from the left, or grouped from the right. Think of it as meaning “they associate with the expression to their left,” or “they associate with the expression to their right.”

  • Operator Functions

    Classes and structures can provide their own implementations of existing operators. This is known as overloading the existing operators.

    struct Vector2D {
      var x = 0.0, y = 0.0
    }
    func + (left: Vector2D, right: Vector2D) -> Vector2D {
      return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
    
  • Prefix and Postfix Operators

    Classes and structures can also provide implementations of the standard unary operators. Unary operators operate on a single target. They are prefix if they precede their target (such as -a) and postfix operators if they follow their target (such as i++).

    You implement a prefix or postfix unary operator by writing the prefix or postfix modifier before the func keyword when declaring the operator function:

    prefix func - (vector: Vector2D) -> Vector2D {
      return Vector2D(x: -vector.x, y: -vector.y)
    }
    
  • Compound Assignment Operators

    Compound assignment operators combine assignment (=) with another operation. For example, the addition assignment operator (+=) combines addition and assignment into a single operation. You mark a compound assignment operator’s left input parameter as inout, because the parameter’s value will be modified directly from within the operator function.

    func += (inout left: Vector2D, right: Vector2D) {
      left = left + right
    }
    

    You can combine assignment with either the prefix or postfix modifier, as in this implementation of the prefix increment operator (++a) for Vector2D instances:

    prefix func ++ (inout vector: Vector2D) -> Vector2D {
      vector += Vector2D(x: 1.0, y: 1.0)
      return vector
    }
    
  • Can we overload = or a?b:c operator?

    It is not possible to overload the default assignment operator (=). Only the compound assignment operators can be overloaded. Similarly, the ternary conditional operator (a ? b : c) cannot be overloaded.

  • Equivalence Operators

    Custom classes and structures do not receive a default implementation of the equivalence operators, known as the “equal to” operator (==) and “not equal to” operator (!=). It is not possible for Swift to guess what would qualify as “equal” for your own custom types, because the meaning of “equal” depends on the roles that those types play in your code.

    To use the equivalence operators to check for equivalence of your own custom type, provide an implementation of the operators in the same way as for other infix operators:

    func == (left: Vector2D, right: Vector2D) -> Bool {
      return (left.x == right.x) && (left.y == right.y)
    }
    func != (left: Vector2D, right: Vector2D) -> Bool {
      return !(left == right)
    }
    
  • Custom Operators

    You can declare and implement your own custom operators in addition to the standard operators provided by Swift. For a list of characters that can be used to define custom operators, see Operators.

    New operators are declared at a global level using the operator keyword, and are marked with the prefix, infix or postfix modifiers:

    prefix operator +++ {}
    
  • Precedence and Associativity for Custom Infix Operators

    Custom infix operators can also specify a precedence and an associativity.

    • The possible values for associativity are left, right, and none. Left-associative operators associate to the left if written next to other left-associative operators of the same precedence. Similarly, right-associative operators associate to the right if written next to other right-associative operators of the same precedence. Non-associative operators cannot be written next to other operators with the same precedence. The associativity value defaults to none if it is not specified.

    • The precedence value defaults to 100 if it is not specified.

    The following example defines a new custom infix operator called +-, with left associativity and a precedence of 140:

    infix operator +- { associativity left precedence 140 }
    

    You do not specify a precedence when defining a prefix or postfix operator. However, if you apply both a prefix and a postfix operator to the same operand, the postfix operator is applied first.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末板熊,一起剝皮案震驚了整個(gè)濱河市徘键,隨后出現(xiàn)的幾起案子晚吞,更是在濱河造成了極大的恐慌,老刑警劉巖弊决,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件周伦,死亡現(xiàn)場離奇詭異宫莱,居然都是意外死亡只酥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門遍希,熙熙樓的掌柜王于貴愁眉苦臉地迎上來等曼,“玉大人,你說我怎么就攤上這事凿蒜〗” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵废封,是天一觀的道長州泊。 經(jīng)常有香客問我,道長漂洋,這世上最難降的妖魔是什么遥皂? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任力喷,我火速辦了婚禮,結(jié)果婚禮上渴肉,老公的妹妹穿的比我還像新娘冗懦。我一直安慰自己爽冕,他們只是感情好仇祭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颈畸,像睡著了一般乌奇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眯娱,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天礁苗,我揣著相機(jī)與錄音,去河邊找鬼徙缴。 笑死试伙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的于样。 我是一名探鬼主播疏叨,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼穿剖!你這毒婦竟也來了蚤蔓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤糊余,失蹤者是張志新(化名)和其女友劉穎秀又,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贬芥,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吐辙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蘸劈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袱讹。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖昵时,靈堂內(nèi)的尸體忽然破棺而出捷雕,到底是詐尸還是另有隱情,我是刑警寧澤壹甥,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布救巷,位于F島的核電站,受9級特大地震影響句柠,放射性物質(zhì)發(fā)生泄漏浦译。R本人自食惡果不足惜棒假,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望精盅。 院中可真熱鬧帽哑,春花似錦、人聲如沸叹俏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粘驰。三九已至屡谐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝌数,已是汗流浹背愕掏。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顶伞,地道東北人饵撑。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像唆貌,于是被迫代替她去往敵國和親滑潘。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • PLEASE READ THE FOLLOWING APPLE DEVELOPER PROGRAM LICENSE...
    念念不忘的閱讀 13,433評論 5 6
  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,389評論 0 23
  • ? 斯圖加特(Stuttgart)位于德國西南部的巴登-符登堡州(Baden-Württemberg)中部挠锥,靠近黑...
    德邇德語閱讀 598評論 0 0
  • 保持正念的練習(xí)众羡,書寫的文字與內(nèi)容減少,思想與情緒同減蓖租。覺照與覺受增加粱侣。知道所有的感受和念頭都是來去自如,此起彼伏...
    夏虹正如閱讀 208評論 0 0
  • 文/夏蓮 四月的天空 澄澈純凈 一眼望穿 斜斜的雨絲 慢慢浸濕女孩的長發(fā) 為你流連忘返 細(xì)雨中認(rèn)真等待 白皙的手慢...
    周小錦閱讀 353評論 3 6