YouTip LogoYouTip

Swift Generics

Swift provides generics to help you write flexible and reusable functions and types.\\n\\nThe Swift standard library is built with generic code.\\n\\nSwift's Array and Dictionary types are both generic collections.\\n\\nYou can create an Int array, a String array, or even an array of any other Swift data type.\\n\\nThe following example is a non-generic function exchange used to swap two Int values:\\n\\n## Instance\\n\\nfunc swapTwoInts(_ a: inout Int, _ b: inout Int){let temporaryA = a a = b b = temporaryA}var numb1 = 100 var numb2 = 200 print("Data before swap: (numb1) and (numb2)")swapTwoInts(&numb1, &numb2)print("Data after swap: (numb1) and (numb2)")\\n\\nThe output of the above program execution is:\\n\\nData before swap: 100 and 200Data after swap: 200 and 100\\nThe above example only applies to swapping variables of the Int type. If you want to swap two String values or Double values, you have to write a corresponding function, such as swapTwoStrings(_:_:) and swapTwoDoubles(_:_:), as shown below:\\n\\n## String and Double value swap functions\\n\\nfunc swapTwoStrings(_ a: inout String, _ b: inout String){let temporaryA = a a = b b = temporaryA}func swapTwoDoubles(_ a: inout Double, _ b: inout Double){let temporaryA = a a = b b = temporaryA}\\n\\nLooking at the above code, their functional code is the same, only the types are different. At this time, we can use generics to avoid writing duplicate code.\\n\\nGenerics use placeholder type names (represented here by the letter T) to replace actual type names (such as Int, String, or Double).\\n\\nfunc swapTwoValues(_ a: inout T, _ b: inout T)\\nswapTwoValues is followed by a placeholder type name (T), enclosed in angle brackets (``). These angle brackets tell Swift that T is a placeholder type name within the definition of the swapTwoValues(_:_:) function, so Swift will not look for an actual type named T.\\n\\nThe following example is a generic function exchange used to swap two Int and String values:\\n\\n## Instance\\n\\nfunc swapTwoValues(_ a: inout T, _ b: inout T){let temporaryA = a a = b b = temporaryA}var numb1 = 100 var numb2 = 200 print("Data before swap: (numb1) and (numb2)")swapTwoValues(&numb1, &numb2)print("Data after swap: (numb1) and (numb2)")var str1 = "A"var str2 = "B"print("Data before swap: (str1) and (str2)")swapTwoValues(&str1, &str2)print("Data after swap: (str1) and (str2)")\\n\\nThe output of the above program execution is:\\n\\nData before swap: 100 and 200Data after swap: 200 and 100Data before swap: A and B Data after swap: B and A\\n\\n* * *\\n\\n## Generic Types\\n\\nSwift allows you to define your own generic types.\\n\\nCustom classes, structures, and enumerations can work with any type, just like Array and Dictionary.\\n\\nNext, let's write a generic collection type named Stack. A stack only allows adding new elements to the end of the collection (called pushing), and also only allows removing elements from the end (called popping).\\n\\n!(#)\\n\\nThe image is parsed from left to right as follows:\\n\\n* Three values are in the stack.\\n* A fourth value is pushed onto the top of the stack.\\n* There are now four values in the stack, and the most recently pushed value is at the top.\\n* The top value in the stack is removed, or popped.\\n* After removing one value, the stack now has only three values again.\\n\\nThe following example is a non-generic version of a stack, using an Int stack as an example:\\n\\n## Int Stack\\n\\nstruct IntStack{var items = ()mutating func push(_ item: Int){items.append(item)}mutating func pop() ->Int{return items.removeLast()}}\\n\\nThis structure uses an Array property named items to store values in the stack. Stack provides two methods: push(_:) and pop(), used to push values onto the stack and remove values from the stack. These methods are marked as mutating because they need to modify the structure's items array.\\n\\nThe IntStack structure above can only be used for Int types. However, you can define a generic Stack structure that can handle values of any type.\\n\\nHere is the generic version of the same code:\\n\\n## Generic Stack\\n\\nstruct Stack{var items = ()mutating func push(_ item: Element){items.append(item)}mutating func pop() ->Element{return items.removeLast()}}var stackOfStrings = Stack()print("Push string element onto the stack: ")stackOfStrings.push("google")stackOfStrings.push("tutorial")print(stackOfStrings.items); let deletetos = stackOfStrings.pop()print("Pop element: " + deletetos)var stackOfInts = Stack()print("Push integer element onto the stack: ")stackOfInts.push(1)stackOfInts.push(2)print(stackOfInts.items);\\n\\nThe instance execution result is:\\n\\nPush string element onto the stack: ["google", "tutorial"]Pop element: tutorial Push integer element onto the stack: [1, 2]\\nStack is basically the same as IntStack, with the placeholder type parameter Element replacing the actual Int type.\\n\\nIn the above instance, Element is used as a placeholder in the following three places:\\n\\n* Creating the **items** property, initializing it with an empty array of type **Element**.\\n* Specifying that the type of the sole parameter **item** of the **push(_:)** method must be of type **Element**.\\n* Specifying that the return type of the **pop()** method must be of type **Element**.\\n\\n* * *\\n\\n## Extending Generic Types\\n\\nWhen you extend a generic type (using the extension keyword), you do not need to provide a list of type parameters in the extension's definition. More conveniently, the type parameter list declared in the original type definition is available in the extension, and these parameter names from the original type will be used as references to the type parameters in the original definition.\\n\\nThe following example extends the generic type Stack, adding a read-only computed property named topItem, which will return the top element of the current stack without removing it from the stack:\\n\\n## Generics\\n\\nstruct Stack{var items = ()mutating func push(_ item: Element){items.append(item)}mutating func pop() ->Element{return items.removeLast()}}extension Stack{var topItem: Element? {return items.isEmpty ? nil : items[items.count - 1]}}var stackOfStrings = Stack()print("Push string element onto the stack: ")stackOfStrings.push("google")stackOfStrings.push("tutorial")if let topItem = stackOfStrings.topItem{print("The top element in the stack is:(topItem).")}print(stackOfStrings.items)\\n\\nIn the instance, the topItem property returns an optional value of type Element. When the stack is empty, topItem returns nil; when the stack is not empty, topItem returns the last element in the items array.\\n\\nThe output of the above program execution is:\\n\\nPush string element onto the stack: The top element in the stack is: tutorial.["google", "tutorial"]\\nWe can also specify associated types by extending an existing type.\\n\\nFor example, Swift's Array type already provides an append(_:) method, a count property, and a subscript that accepts an Int type index value to retrieve its elements. These three functionalities all meet the requirements of the Container protocol, so you can simply extend Array by declaring that Array adopts the protocol.\\n\\nThe following instance creates an empty extension:\\n\\nextension Array: Container {}\\n\\n* * *\\n\\n## Type Constraints\\n\\nType constraints specify that a type parameter must inherit from a specified class, or follow a specific protocol or protocol composition.\\n\\n### Type Constraint Syntax\\n\\nYou can write a type constraint after a type parameter name, separated by a colon, as part of the type parameter chain. The basic syntax for such type constraints acting on generic functions is as follows (same syntax for generic types):\\n\\nfunc someFunction(someT: T, someU: U) { // This is the function body part of the generic function}\\nThe above function has two type parameters. The first type parameter T has a type constraint requiring T to be a subclass of SomeClass; the second type parameter U has a type constraint requiring U to conform to the SomeProtocol protocol.\\n\\n### Instance\\n\\n## Generics\\n\\nfunc findIndex(ofString valueToFind: String, in array: ) ->Int? {for(index, value)in array.enumerated(){if value == valueToFind{return index}}return nil}let strings = ["google", "weibo", "taobao", "tutorial", "facebook"]if let foundIndex = findIndex(ofString: "tutorial", in: strings){print("tutorial has an index of (foundIndex)")}\\n\\nIndex subscripts start at 0.\\n\\nThe output of the above program execution is:\\n\\ntutorial has an index of 3\\n\\n* * *\\n\\n## Associated Types\\n\\nSwift uses the associatedtype keyword to set up associated type instances.\\n\\nThe following example defines a Container protocol, which defines an associated type ItemType.\\n\\nThe Container protocol only specifies three functionalities that any type conforming to the
← Swift Access ControlSwift Protocols β†’