Initial commit
This commit is contained in:
661
skills/programming-swift/LanguageGuide/Methods.md
Normal file
661
skills/programming-swift/LanguageGuide/Methods.md
Normal file
@@ -0,0 +1,661 @@
|
||||
# Methods
|
||||
|
||||
Define and call functions that are part of an instance or type.
|
||||
|
||||
*Methods* are functions that are associated with a particular type.
|
||||
Classes, structures, and enumerations can all define instance methods,
|
||||
which encapsulate specific tasks and functionality for working with an instance of a given type.
|
||||
Classes, structures, and enumerations can also define type methods,
|
||||
which are associated with the type itself.
|
||||
Type methods are similar to class methods in Objective-C.
|
||||
|
||||
The fact that structures and enumerations can define methods in Swift
|
||||
is a major difference from C and Objective-C.
|
||||
In Objective-C, classes are the only types that can define methods.
|
||||
In Swift, you can choose whether to define a class, structure, or enumeration,
|
||||
and still have the flexibility to define methods on the type you create.
|
||||
|
||||
## Instance 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.
|
||||
Instance methods have exactly the same syntax as functions,
|
||||
as described in <doc:Functions>.
|
||||
|
||||
You write an instance method within the opening and closing braces of the type it belongs to.
|
||||
An instance method has implicit access to all other instance methods and properties of that type.
|
||||
An instance method can be called only on a specific instance of the type it belongs to.
|
||||
It can't be called in isolation without an existing instance.
|
||||
|
||||
Here's an example that defines a simple `Counter` class,
|
||||
which can be used to count the number of times an action occurs:
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count = 0
|
||||
func increment() {
|
||||
count += 1
|
||||
}
|
||||
func increment(by amount: Int) {
|
||||
count += amount
|
||||
}
|
||||
func reset() {
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `instanceMethods`
|
||||
|
||||
```swifttest
|
||||
-> class Counter {
|
||||
var count = 0
|
||||
func increment() {
|
||||
count += 1
|
||||
}
|
||||
func increment(by amount: Int) {
|
||||
count += amount
|
||||
}
|
||||
func reset() {
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
The `Counter` class defines three instance methods:
|
||||
|
||||
- `increment()` increments the counter by `1`.
|
||||
- `increment(by: Int)` increments the counter by a specified integer amount.
|
||||
- `reset()` resets the counter to zero.
|
||||
|
||||
The `Counter` class also declares a variable property, `count`,
|
||||
to keep track of the current counter value.
|
||||
|
||||
You call instance methods with the same dot syntax as properties:
|
||||
|
||||
```swift
|
||||
let counter = Counter()
|
||||
// the initial counter value is 0
|
||||
counter.increment()
|
||||
// the counter's value is now 1
|
||||
counter.increment(by: 5)
|
||||
// the counter's value is now 6
|
||||
counter.reset()
|
||||
// the counter's value is now 0
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `instanceMethods`
|
||||
|
||||
```swifttest
|
||||
-> let counter = Counter()
|
||||
/> the initial counter value is \(counter.count)
|
||||
</ the initial counter value is 0
|
||||
-> counter.increment()
|
||||
/> the counter's value is now \(counter.count)
|
||||
</ the counter's value is now 1
|
||||
-> counter.increment(by: 5)
|
||||
/> the counter's value is now \(counter.count)
|
||||
</ the counter's value is now 6
|
||||
-> counter.reset()
|
||||
/> the counter's value is now \(counter.count)
|
||||
</ the counter's value is now 0
|
||||
```
|
||||
-->
|
||||
|
||||
Function parameters can have both a name (for use within the function's body)
|
||||
and an argument label (for use when calling the function),
|
||||
as described in <doc:Functions#Function-Argument-Labels-and-Parameter-Names>.
|
||||
The same is true for method parameters,
|
||||
because methods are just functions that are associated with a type.
|
||||
|
||||
### The self Property
|
||||
|
||||
Every instance of a type has an implicit property called `self`,
|
||||
which is exactly equivalent to the instance itself.
|
||||
You use the `self` property to refer to the current instance
|
||||
within its own instance methods.
|
||||
|
||||
The `increment()` method in the example above could have been written like this:
|
||||
|
||||
```swift
|
||||
func increment() {
|
||||
self.count += 1
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `instanceMethodsIncrement`
|
||||
|
||||
```swifttest
|
||||
>> class Counter {
|
||||
>> var count: Int = 0
|
||||
func increment() {
|
||||
self.count += 1
|
||||
}
|
||||
>> }
|
||||
```
|
||||
-->
|
||||
|
||||
<!--
|
||||
NOTE: I'm slightly cheating with my testing of this excerpt, but it works!
|
||||
-->
|
||||
|
||||
In practice, you don't need to write `self` in your code very often.
|
||||
If you don't explicitly write `self`,
|
||||
Swift assumes that you are referring to a property or method of the current instance
|
||||
whenever you use a known property or method name within a method.
|
||||
This assumption is demonstrated by the use of `count` (rather than `self.count`)
|
||||
inside the three instance methods for `Counter`.
|
||||
|
||||
The main exception to this rule occurs when a parameter name for an instance method
|
||||
has the same name as a property of that instance.
|
||||
In this situation, the parameter name takes precedence,
|
||||
and it becomes necessary to refer to the property in a more qualified way.
|
||||
You use the `self` property to
|
||||
distinguish between the parameter name and the property name.
|
||||
|
||||
Here, `self` disambiguates between
|
||||
a method parameter called `x` and an instance property that's also called `x`:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
func isToTheRightOf(x: Double) -> Bool {
|
||||
return self.x > x
|
||||
}
|
||||
}
|
||||
let somePoint = Point(x: 4.0, y: 5.0)
|
||||
if somePoint.isToTheRightOf(x: 1.0) {
|
||||
print("This point is to the right of the line where x == 1.0")
|
||||
}
|
||||
// Prints "This point is to the right of the line where x == 1.0".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `self`
|
||||
|
||||
```swifttest
|
||||
-> struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
func isToTheRightOf(x: Double) -> Bool {
|
||||
return self.x > x
|
||||
}
|
||||
}
|
||||
-> let somePoint = Point(x: 4.0, y: 5.0)
|
||||
-> if somePoint.isToTheRightOf(x: 1.0) {
|
||||
print("This point is to the right of the line where x == 1.0")
|
||||
}
|
||||
<- This point is to the right of the line where x == 1.0
|
||||
```
|
||||
-->
|
||||
|
||||
Without the `self` prefix,
|
||||
Swift would assume that both uses of `x` referred to the method parameter called `x`.
|
||||
|
||||
### Modifying Value Types from Within Instance Methods
|
||||
|
||||
Structures and enumerations are *value types*.
|
||||
By default, the properties of a value type can't be modified from within its instance methods.
|
||||
|
||||
<!--
|
||||
TODO: find out why. once I actually know why, explain it.
|
||||
-->
|
||||
|
||||
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.
|
||||
|
||||
You can opt in to this behavior by placing the `mutating` keyword
|
||||
before the `func` keyword for that method:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
}
|
||||
}
|
||||
var somePoint = Point(x: 1.0, y: 1.0)
|
||||
somePoint.moveBy(x: 2.0, y: 3.0)
|
||||
print("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
// Prints "The point is now at (3.0, 4.0)".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `selfStructures`
|
||||
|
||||
```swifttest
|
||||
-> struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
}
|
||||
}
|
||||
-> var somePoint = Point(x: 1.0, y: 1.0)
|
||||
-> somePoint.moveBy(x: 2.0, y: 3.0)
|
||||
-> print("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
<- The point is now at (3.0, 4.0)
|
||||
```
|
||||
-->
|
||||
|
||||
The `Point` structure above defines a mutating `moveBy(x:y:)` method,
|
||||
which moves a `Point` instance by a certain amount.
|
||||
Instead of returning a new point,
|
||||
this method actually modifies the point on which it's called.
|
||||
The `mutating` keyword is added to its definition
|
||||
to enable it to modify its properties.
|
||||
|
||||
Note that you can't call a mutating method on a constant of structure type,
|
||||
because its properties can't be changed, even if they're variable properties,
|
||||
as described in <doc:Properties#Stored-Properties-of-Constant-Structure-Instances>:
|
||||
|
||||
```swift
|
||||
let fixedPoint = Point(x: 3.0, y: 3.0)
|
||||
fixedPoint.moveBy(x: 2.0, y: 3.0)
|
||||
// this will report an error
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `selfStructures-err`
|
||||
|
||||
```swifttest
|
||||
>> struct Point {
|
||||
>> var x = 0.0, y = 0.0
|
||||
>> mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
>> x += deltaX
|
||||
>> y += deltaY
|
||||
>> }
|
||||
>> }
|
||||
-> let fixedPoint = Point(x: 3.0, y: 3.0)
|
||||
-> fixedPoint.moveBy(x: 2.0, y: 3.0)
|
||||
!$ error: cannot use mutating member on immutable value: 'fixedPoint' is a 'let' constant
|
||||
!! fixedPoint.moveBy(x: 2.0, y: 3.0)
|
||||
!! ~~~~~~~~~~ ^
|
||||
!$ note: change 'let' to 'var' to make it mutable
|
||||
!! let fixedPoint = Point(x: 3.0, y: 3.0)
|
||||
!! ^~~
|
||||
!! var
|
||||
// this will report an error
|
||||
```
|
||||
-->
|
||||
|
||||
<!--
|
||||
TODO: talk about nonmutating as well.
|
||||
Struct setters are implicitly 'mutating' by default and thus don't work on 'let's.
|
||||
However, JoeG says that this ought to work
|
||||
if the setter for the computed property is explicitly defined as @!mutating.
|
||||
-->
|
||||
|
||||
### Assigning to self Within a Mutating Method
|
||||
|
||||
Mutating methods can assign an entirely new instance to the implicit `self` property.
|
||||
The `Point` example shown above could have been written in the following way instead:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
self = Point(x: x + deltaX, y: y + deltaY)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `selfStructuresAssign`
|
||||
|
||||
```swifttest
|
||||
-> struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
self = Point(x: x + deltaX, y: y + deltaY)
|
||||
}
|
||||
}
|
||||
>> var somePoint = Point(x: 1.0, y: 1.0)
|
||||
>> somePoint.moveBy(x: 2.0, y: 3.0)
|
||||
>> print("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
<< The point is now at (3.0, 4.0)
|
||||
```
|
||||
-->
|
||||
|
||||
This version of the mutating `moveBy(x:y:)` method creates a new structure
|
||||
whose `x` and `y` values are set to the target location.
|
||||
The end result of calling this alternative version of the method
|
||||
will be exactly the same as for calling the earlier version.
|
||||
|
||||
Mutating methods for enumerations can set the implicit `self` parameter to be
|
||||
a different case from the same enumeration:
|
||||
|
||||
```swift
|
||||
enum TriStateSwitch {
|
||||
case off, low, high
|
||||
mutating func next() {
|
||||
switch self {
|
||||
case .off:
|
||||
self = .low
|
||||
case .low:
|
||||
self = .high
|
||||
case .high:
|
||||
self = .off
|
||||
}
|
||||
}
|
||||
}
|
||||
var ovenLight = TriStateSwitch.low
|
||||
ovenLight.next()
|
||||
// ovenLight is now equal to .high
|
||||
ovenLight.next()
|
||||
// ovenLight is now equal to .off
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `selfEnumerations`
|
||||
|
||||
```swifttest
|
||||
-> enum TriStateSwitch {
|
||||
case off, low, high
|
||||
mutating func next() {
|
||||
switch self {
|
||||
case .off:
|
||||
self = .low
|
||||
case .low:
|
||||
self = .high
|
||||
case .high:
|
||||
self = .off
|
||||
}
|
||||
}
|
||||
}
|
||||
-> var ovenLight = TriStateSwitch.low
|
||||
-> ovenLight.next()
|
||||
// ovenLight is now equal to .high
|
||||
-> ovenLight.next()
|
||||
// ovenLight is now equal to .off
|
||||
```
|
||||
-->
|
||||
|
||||
This example defines an enumeration for a three-state switch.
|
||||
The switch cycles between three different power states
|
||||
(`off`, `low` and `high`)
|
||||
every time its `next()` method is called.
|
||||
|
||||
## Type Methods
|
||||
|
||||
Instance methods, as described above,
|
||||
are methods that you call on an instance of a particular type.
|
||||
You can also define methods that are called on the type itself.
|
||||
These kinds of methods are called *type methods*.
|
||||
You indicate type methods by writing
|
||||
the `static` keyword before the method's `func` keyword.
|
||||
Classes can use the `class` keyword instead,
|
||||
to allow subclasses to override the superclass’s implementation of that method.
|
||||
|
||||
> Note: In Objective-C, you can define type-level methods only for Objective-C classes.
|
||||
> In Swift, you can define type-level methods for all classes, structures, and enumerations.
|
||||
> Each type method is explicitly scoped to the type it supports.
|
||||
|
||||
Type methods are called with dot syntax, like instance methods.
|
||||
However, you call type methods on the type, not on an instance of that type.
|
||||
Here's how you call a type method on a class called `SomeClass`:
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
SomeClass.someTypeMethod()
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `typeMethods`
|
||||
|
||||
```swifttest
|
||||
-> class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
-> SomeClass.someTypeMethod()
|
||||
```
|
||||
-->
|
||||
|
||||
Within the body of a type method,
|
||||
the implicit `self` property refers to the type itself,
|
||||
rather than an instance of that type.
|
||||
This means that you can use `self` to disambiguate between
|
||||
type properties and type method parameters,
|
||||
just as you do for instance properties and instance method parameters.
|
||||
|
||||
More generally, any unqualified method and property names that you use
|
||||
within the body of a type method will refer to other type-level methods and properties.
|
||||
A type method can call another type method with the other method's name,
|
||||
without needing to prefix it with the type name.
|
||||
Similarly, type methods on structures and enumerations can access type properties
|
||||
by using the type property's name without a type name prefix.
|
||||
|
||||
The example below defines a structure called `LevelTracker`,
|
||||
which tracks a player's progress through the different levels or stages of a game.
|
||||
It's a single-player game,
|
||||
but can store information for multiple players on a single device.
|
||||
|
||||
All of the game's levels (apart from level one) are locked when the game is first played.
|
||||
Every time a player finishes a level,
|
||||
that level is unlocked for all players on the device.
|
||||
The `LevelTracker` structure uses type properties and methods
|
||||
to keep track of which levels of the game have been unlocked.
|
||||
It also tracks the current level for an individual player.
|
||||
|
||||
```swift
|
||||
struct LevelTracker {
|
||||
static var highestUnlockedLevel = 1
|
||||
var currentLevel = 1
|
||||
|
||||
static func unlock(_ level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
|
||||
static func isUnlocked(_ level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
mutating func advance(to level: Int) -> Bool {
|
||||
if LevelTracker.isUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `typeMethods`
|
||||
|
||||
```swifttest
|
||||
-> struct LevelTracker {
|
||||
static var highestUnlockedLevel = 1
|
||||
var currentLevel = 1
|
||||
|
||||
-> static func unlock(_ level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
|
||||
-> static func isUnlocked(_ level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
|
||||
-> @discardableResult
|
||||
mutating func advance(to level: Int) -> Bool {
|
||||
if LevelTracker.isUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
The `LevelTracker` structure keeps track of the highest level that any player has unlocked.
|
||||
This value is stored in a type property called `highestUnlockedLevel`.
|
||||
|
||||
`LevelTracker` also defines two type functions to work with
|
||||
the `highestUnlockedLevel` property.
|
||||
The first is a type function called `unlock(_:)`,
|
||||
which updates the value of `highestUnlockedLevel` whenever a new level is unlocked.
|
||||
The second is a convenience type function called `isUnlocked(_:)`,
|
||||
which returns `true` if a particular level number is already unlocked.
|
||||
(Note that these type methods can access the `highestUnlockedLevel` type property
|
||||
without your needing to write it as `LevelTracker.highestUnlockedLevel`.)
|
||||
|
||||
In addition to its type property and type methods,
|
||||
`LevelTracker` tracks an individual player's progress through the game.
|
||||
It uses an instance property called `currentLevel` to track
|
||||
the level that a player is currently playing.
|
||||
|
||||
To help manage the `currentLevel` property,
|
||||
`LevelTracker` defines an instance method called `advance(to:)`.
|
||||
Before updating `currentLevel`,
|
||||
this method checks whether the requested new level is already unlocked.
|
||||
The `advance(to:)` method returns a Boolean value to indicate
|
||||
whether or not it was actually able to set `currentLevel`.
|
||||
Because it's not necessarily a mistake for
|
||||
code that calls the `advance(to:)` method
|
||||
to ignore the return value,
|
||||
this function is marked with the `@discardableResult` attribute.
|
||||
For more information about this attribute,
|
||||
see <doc:Attributes>.
|
||||
|
||||
The `LevelTracker` structure is used with the `Player` class, shown below,
|
||||
to track and update the progress of an individual player:
|
||||
|
||||
```swift
|
||||
class Player {
|
||||
var tracker = LevelTracker()
|
||||
let playerName: String
|
||||
func complete(level: Int) {
|
||||
LevelTracker.unlock(level + 1)
|
||||
tracker.advance(to: level + 1)
|
||||
}
|
||||
init(name: String) {
|
||||
playerName = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `typeMethods`
|
||||
|
||||
```swifttest
|
||||
-> class Player {
|
||||
var tracker = LevelTracker()
|
||||
let playerName: String
|
||||
func complete(level: Int) {
|
||||
LevelTracker.unlock(level + 1)
|
||||
tracker.advance(to: level + 1)
|
||||
}
|
||||
init(name: String) {
|
||||
playerName = name
|
||||
}
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
The `Player` class creates a new instance of `LevelTracker`
|
||||
to track that player's progress.
|
||||
It also provides a method called `complete(level:)`,
|
||||
which is called whenever a player completes a particular level.
|
||||
This method unlocks the next level for all players
|
||||
and updates the player's progress to move them to the next level.
|
||||
(The Boolean return value of `advance(to:)` is ignored,
|
||||
because the level is known to have been unlocked
|
||||
by the call to `LevelTracker.unlock(_:)` on the previous line.)
|
||||
|
||||
You can create an instance of the `Player` class for a new player,
|
||||
and see what happens when the player completes level one:
|
||||
|
||||
```swift
|
||||
var player = Player(name: "Argyrios")
|
||||
player.complete(level: 1)
|
||||
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
|
||||
// Prints "highest unlocked level is now 2".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `typeMethods`
|
||||
|
||||
```swifttest
|
||||
-> var player = Player(name: "Argyrios")
|
||||
-> player.complete(level: 1)
|
||||
-> print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
|
||||
<- highest unlocked level is now 2
|
||||
```
|
||||
-->
|
||||
|
||||
If you create a second player, whom you try to move to a level
|
||||
that's not yet unlocked by any player in the game,
|
||||
the attempt to set the player's current level fails:
|
||||
|
||||
```swift
|
||||
player = Player(name: "Beto")
|
||||
if player.tracker.advance(to: 6) {
|
||||
print("player is now on level 6")
|
||||
} else {
|
||||
print("level 6 hasn't yet been unlocked")
|
||||
}
|
||||
// Prints "level 6 hasn't yet been unlocked".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `typeMethods`
|
||||
|
||||
```swifttest
|
||||
-> player = Player(name: "Beto")
|
||||
-> if player.tracker.advance(to: 6) {
|
||||
print("player is now on level 6")
|
||||
} else {
|
||||
print("level 6 hasn't yet been unlocked")
|
||||
}
|
||||
<- level 6 hasn't yet been unlocked
|
||||
```
|
||||
-->
|
||||
|
||||
<!--
|
||||
TODO: Method Binding
|
||||
--------------------
|
||||
|
||||
TODO: you can get a function that refers to a method, either with or without the 'self' argument already being bound:
|
||||
class C {
|
||||
func foo(x: Int) -> Float { ... }
|
||||
}
|
||||
var c = C()
|
||||
var boundFunc = c.foo // a function with type (Int) -> Float
|
||||
var unboundFunc = C.foo // a function with type (C) -> (Int) -> Float
|
||||
|
||||
TODO: selector-style methods can be referenced as foo.bar:bas:
|
||||
(see Doug's comments from the 2014-03-12 release notes)
|
||||
-->
|
||||
|
||||
<!--
|
||||
This source file is part of the Swift.org open source project
|
||||
|
||||
Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
|
||||
Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
|
||||
See https://swift.org/LICENSE.txt for license information
|
||||
See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
-->
|
||||
Reference in New Issue
Block a user