* 내가 읽으려고 내 맘대로 번역한 글.
* 원문 : https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html

 

 

 

Optional Chaining

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.

옵셔널 체이닝은 옵셔널이 nil 일 수도 있는 속성, 메소드, 첨자에 대한 조회와 호출하는 과정이다.

옵셔널이 값을 가지고 있다면, 속성, 메소드, 첨자 호출은 성공한다.

옵셔널이 nil 이면 속성, 메소드, 첨자 호출은 nil 을 리턴한다.

다중 조회는 체인을 묶어서 할수 있고, 하나의 체인이라도 nil 이면 전체 체인은 정상적으로 실패한다.

 

NOTE

Optional chaining in Swift is similar to messaging nil in Objective-C, but in a way that works for any type, and that can be checked for success or failure.

swift 에서 옵셔널 체이닝은 objective-c에서 nil 메세지 전달과 비슷하지만,

swift 는 모든 타입에 동작하고, 성공 또는 실패를 확인할수 있다.

Optional Chaining as an Alternative to Forced Unwrapping

You specify optional chaining by placing a question mark (?) after the optional value on which you wish to call a property, method or subscript if the optional is non-nil. This is very similar to placing an exclamation mark (!) after an optional value to force the unwrapping of its value. The main difference is that optional chaining fails gracefully when the optional is nil, whereas forced unwrapping triggers a runtime error when the optional is nil.

옵셔널이 널이 아닌 속성, 메소드, 첨자를 호출할때 옵셔널 값 뒤에 물음표(?)를 붙여서 옵셔널 체이닝을 표시할수 있다.

이것은 옵셔널 값 뒤에 물음표(!)를 붙여 강제로 값을 꺼내는 언래핑과 매우 비슷하다.

큰 차이점은 옵셔널 체이닝은 옵셔널이 널일때 우아하게 실패하고, 강제 언래핑은 옵셔널이 널일때 런타임 에러가 발생한다는 것이다.

 

To reflect the fact that optional chaining can be called on a nil value, the result of an optional chaining call is always an optional value, even if the property, method, or subscript you are querying returns a non-optional value. You can use this optional return value to check whether the optional chaining call was successful (the returned optional contains a value), or did not succeed due to a nil value in the chain (the returned optional value is nil).

옵셔널 체이닝이 널값에서 호출될수 있다는걸 반영하기 위하여,

비록 너가 조회하는 속성, 메소드, 첨자가 옵셔널아닌 값을 리턴하더라도, 옵셔널 체이닝 호출 결과는 항상 옵셔널 값이다.

이 리턴된 옵셔널 값을 검사하여 옵셔널 체이닝 호출이 성공 (리턴된 옵셔널이 값을 가지고 있음) 했는지,

또는 체인중에 하나가 널이라서 성공하지 못했는지 (리턴된 옵셔널 값이 널임) 여부를 검사할수 있다.

 

Specifically, the result of an optional chaining call is of the same type as the expected return value, but wrapped in an optional. A property that normally returns an Int will return an Int? when accessed through optional chaining.

특히, 옵셔널 체이닝 호출의 결과는 예상했던 리턴값과 동일한 타입이지만, 옵셔널로 감싸져 있다. 

보통 Int를 리턴하는 속성은 옵셔널 체이닝을 통해서 접근될때 Int? 를 리턴한다.

 

The next several code snippets demonstrate how optional chaining differs from forced unwrapping and enables you to check for success.

다음 몇개의 코드 조각은 옵셔널 체이닝이 강제 언래핑과 어떻게 다른지 보여주고, 성공을 검사할수 있게 한다.

 

First, two classes called Person and Residence are defined:

먼저, Person과 Residence 라는 클래스 2개를 정의하였다.

class Person {
	var residence: Residence?
}

class Residence {
	var numberOfRooms = 1
}

 

Residence instances have a single Int property called numberOfRooms, with a default value of 1. Person instances have an optional residence property of type Residence?.

Residence 인스턴스는 numberOfRooms 라는 하나의 Int 속성을 가지고 있고, 기본값은 1 이다.

Person 인스턴스는 Residence? 타입의 residence 라는 옵셔널 속성을 가지고 있다.

 

If you create a new Person instance, its residence property is default initialized to nil, by virtue of being optional. In the code below, john has a residence property value of nil:

새로운 Person 인스턴스를 만들면, residence 속성은 옵셔널이니까 기본값 nil 로 초기화 된다.

아래 코드에서 john 은 residence 속성 값으로 nil 을 가진다.

let john = Person()

 

If you try to access the numberOfRooms property of this person’s residence, by placing an exclamation mark after residence to force the unwrapping of its value, you trigger a runtime error, because there is no residence value to unwrap:

이 개인의 residence 의 numberOfRooms 속성에 접근하려 할때, residence 뒤에 느낌표를 넣어서 강제로 값을 언래핑 한다면,

residence는 언랩할 값이 없으므로, 런타임 에러가 발생할것이다.

let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

 

The code above succeeds when john.residence has a non-nil value and will set roomCount to an Int value containing the appropriate number of rooms. However, this code always triggers a runtime error when residence is nil, as illustrated above.

위의 코드는 john.residence가 널값이 아니면 성공할것이고, roomCount에 적절한 방의 갯수를 Int 값으로 설정할 것이다.

하지만, 위에서 설명했듯이, 이 코드는 residence가 nil 일때는 항상 런타임 에러가 발생한다

 

Optional chaining provides an alternative way to access the value of numberOfRooms. To use optional chaining, use a question mark in place of the exclamation mark:

옵셔널 체이닝은 numberOfRooms의 값에 접근하는 다른 방법을 제공한다.

옵셔널 체이닝을 사용하기 위해서, 느낌표 자리에 물음표를 사용해라.

if let roomCount = john.residence?.numberOfRooms {
	print("John's residence has \(roomCount) room(s).")
} else {
	print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

 

This tells Swift to “chain” on the optional residence property and to retrieve the value of numberOfRooms if residence exists.

이것은 swift가 옵셔널 residence 속성을 묶고 residence 가 있으면 numberOfRooms 값을 조회하도록 한다.

 

Because the attempt to access numberOfRooms has the potential to fail, the optional chaining attempt returns a value of type Int?, or “optional Int”. When residence is nil, as in the example above, this optional Int will also be nil, to reflect the fact that it was not possible to access numberOfRooms. The optional Int is accessed through optional binding to unwrap the integer and assign the non-optional value to the roomCount variable.

numberOfRooms 에 접근하려는 시도가 실패할수 있으므로, 옵셔널 체이닝은 Int? 또는 optional Int의 값을 리턴하려고 시도한다.

residence가 nil 이면, 위의 예에서 처럼, optional Int는 nil 이 되어, numberOfRooms에 대한 접근이 불가능하다는걸 반영한다.

optional Int 는 정수값을 언랩하기 위해 optional 바인딩을 통해 접근되고 roomCount 변수에 옵셔널이 아닌 값을 할당한다.

 

Note that this is true even though numberOfRooms is a non-optional Int. The fact that it is queried through an optional chain means that the call to numberOfRooms will always return an Int? instead of an Int.

numberOfRooms 가 optional Int 가 아니라도 이것은 적용된다는걸 주목해라.

옵셔널 체인을 통해서 조회된다는것은 numberOfRooms를 호출하면 언제나 Int 대신에 Int? 를 리턴한다는것이다. 

 

You can assign a Residence instance to john.residence, so that it no longer has a nil value:

Residence 인스턴스를 john.residence 에 할당할수 있다. 왜냐면 더이상 nil 값을 가지지 않으니까.

john.residence = Residence()

 

john.residence now contains an actual Residence instance, rather than nil. If you try to access numberOfRooms with the same optional chaining as before, it will now return an Int? that contains the default numberOfRooms value of 1:

john.residence 는 이제 nil이 아니고, 실제로 Residence 인스턴스를 포함한다.

위에서 처럼 옵셔널 체인으로 numberOfRooms에 접근하면, 기본값 1을 포함하는 Int? 를 리턴할것이다.

if let roomCount = john.residence?.numberOfRooms {
	print("John's residence has \(roomCount) room(s).")
} else {
	print("Unable to retrieve the number of rooms.")
}
// Prints "John's residence has 1 room(s)."

 

Defining Model Classes for Optional Chaining

You can use optional chaining with calls to properties, methods, and subscripts that are more than one level deep. This enables you to drill down into subproperties within complex models of interrelated types, and to check whether it is possible to access properties, methods, and subscripts on those subproperties.

속성, 메소드, 첨자들을 한단계 이상 깊게 호출할때 옵셔널 체이닝을 사용할수 있다.

이것은 서로 상관있는 타입의 복잡한 모델들과 같이 서브 속성으로 들어갈수 있게 해주고,

속성, 메소드, 첨자들의 서브 속성에 접근이 가능한지 검사하도록 해준다.

 

The code snippets below define four model classes for use in several subsequent examples, including examples of multilevel optional chaining. These classes expand upon the Person and Residence model from above by adding a Room and Address class, with associated properties, methods, and subscripts.

아래 코드 조각은 4개의 클래스 모델을 정의하고, 다중레벨 옵셔널 체이닝을 포함하여 여러개의 순차적인 예제에 사용한다.

이 클래스들은 위에서 나온 Person 과 Residence 모델을 확장하여,

연관된 속성, 메소드, 첨자들과 함께 Room 과 Address 클래스를 추가하였다.

 

The Person class is defined in the same way as before:

Person 클래스는 앞에서와 같은 방식으로 정의되었다.

class Person {
	var residence: Residence?
}

 

The Residence class is more complex than before. This time, the Residence class defines a variable property called rooms, which is initialized with an empty array of type [Room]:

Residence 클래스는 앞에서보다 좀 더 복잡해졌다.

이번에 Residence 클래스는 rooms 라는 변수 속성을 정의했고, 이것은 Room 타입의 빈 배열로 초기화 되었다.

class Residence {
	var rooms = [Room]()
	var numberOfRooms: Int {
		return rooms.count
	}
	subscript(i: Int) -> Room {
		get {
			return rooms[i]
		}
		set {
			rooms[i] = newValue
		}
	}
	func printNumberOfRooms() {
		print("The number of rooms is \(numberOfRooms)")
	}
	var address: Address?
}

 

Because this version of Residence stores an array of Room instances, its numberOfRooms property is implemented as a computed property, not a stored property. The computed numberOfRooms property simply returns the value of the count property from the rooms array.

이 버젼의 Residence 는 Room 인스턴스의 배열을 저장하기 때문에,

numberOfRooms 속성은 저장속성이 아니고, 계산 속성으로 구현되었다.

numberOfRooms 계산 속성은 단순히 rooms 배열에서 count 속성을 리턴한다.

 

As a shortcut to accessing its rooms array, this version of Residence provides a read-write subscript that provides access to the room at the requested index in the rooms array.

rooms 배열에 접근하는 지름길로써, 이 버젼의 Residence 는 읽기쓰기 가능한 첨자를 제공하여,

rooms 배열에서 요청된 index에 있는 room에 접근할수 있다.

 

This version of Residence also provides a method called printNumberOfRooms, which simply prints the number of rooms in the residence.

이 버젼의 Residence 는 printNumberOfRooms 라는 메소드도 제공해서, 단순히 룸의 갯수를 출력할수 있다.

 

Finally, Residence defines an optional property called address, with a type of Address?. The Address class type for this property is defined below.

마지막으로, Residence 는 Address? 타입의 address 라는 옵셔널 속성을 정의했다.

Address 클래스는 아래에 정의 되어있다.

 

The Room class used for the rooms array is a simple class with one property called name, and an initializer to set that property to a suitable room name:

rooms 배열에 사용되는 Room 클래스는 name 이라는 속성 하나를 가지는 단순한 클래스고,

초기화는 알맞은 룸 이름을 설정한다.

class Room {
	let name: String
	init(name: String) { self.name = name }
}

 

The final class in this model is called Address. This class has three optional properties of type String?. The first two properties, buildingName and buildingNumber, are alternative ways to identify a particular building as part of an address. The third property, street, is used to name the street for that address:

이 모델의 마지막은 Address 이다. 이 클래스는 3개의 String? 타입 옵셔널 속성을 가지고 있다.

처음 2개 속성 buildingName 과 buildingNumber 주소에서 특정 빌딩을 구분하는 두가지 방법이다.

3번째 속성 street 는 주소에 대한 거리의 이름에 사용된다.

class Address {
	var buildingName: String?
	var buildingNumber: String?
	var street: String?
	func buildingIdentifier() -> String? {
		if let buildingNumber = buildingNumber, let street = street {
			return "\(buildingNumber) \(street)"
		} else if buildingName != nil {
			return buildingName
		} else {
			return nil
		}
	}
}

 

The Address class also provides a method called buildingIdentifier(), which has a return type of String?. This method checks the properties of the address and returns buildingName if it has a value, or buildingNumber concatenated with street if both have values, or nil otherwise.

Address 클래스는 buildingIdentifier() 라는 메소드를 제공하여, String? 타입을 리천한다.

이 메소드는 주소의 속성을 검사하여 buildingName이 값을 가지고 있으면 그걸 리턴하고,

buildingNumber 과 street 가 둘다 값을 가지고 있으면, 둘을 합하여 리턴하고,

아니면 nil 을 리턴한다.

 

Accessing Properties Through Optional Chaining

As demonstrated in Optional Chaining as an Alternative to Forced Unwrapping, you can use optional chaining to access a property on an optional value, and to check if that property access is successful.

옵셔널 체이닝을 사용하여 옵셔널 값을 가지는 속성에 접근할수 있고, 그 속성에 대한 접근이 성공했는지를 검사할수 있다.

 

Use the classes defined above to create a new Person instance, and try to access its numberOfRooms property as before:

위에서 정의한 클래스들을 이용해서 Person 인스턴스를 만들었고, numberOfRooms 속성에 접근을 시도해라.

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
	print("John's residence has \(roomCount) room(s).")
} else {
	print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

 

Because john.residence is nil, this optional chaining call fails in the same way as before.

You can also attempt to set a property’s value through optional chaining:

john.residence 가 nil 이라서, 이 옵셔널 체이닝 호출은 전에 본것처럼 실패한다.

옵셔널 체이닝을 통해서 속성에 값을 설정할려고 시도할수 있다.

let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress

 

In this example, the attempt to set the address property of john.residence will fail, because john.residence is currently nil.

이 예제에서, john.residence의 address 속성에 값을 설정하려는 시도는 실패할 것이다.

왜냐면 john.residence가 현재 nil 이기 때문에.

 

The assignment is part of the optional chaining, which means none of the code on the right-hand side of the = operator is evaluated. In the previous example, it’s not easy to see that someAddress is never evaluated, because accessing a constant doesn’t have any side effects. The listing below does the same assignment, but it uses a function to create the address. The function prints “Function was called” before returning a value, which lets you see whether the right-hand side of the = operator was evaluated.

설정은 옵셔널 체이닝의 부분이고, 이것은 = 연산자의 오른쪽에 있는 코드는 평가되지 않는다는 뜻이다.

(이석우 추가 : 왼쪽 옵셔널 체이닝이 nil 이라서 실패하니까, = 연산자 오른쪽은 아예 평가되지 않는다는 뜻인거 같음)

이전 예제에서, someAddress가 평가되지 않는다는걸 알아보기는 쉽지 않다, 왜냐면 상수에 접근하는것은 아무런 부작용이 없으니까.

아래에 나열된 코드는 같은 설정을 하지만, 주소를 생성하기 위해 함수를 이용한다.

이 함수는 값을 리턴하기 전에 'Function was called' 를 출력하는데,

그러면 너는 = 연산자의 오른쪽 코드가 평가되었다는걸 알수 있다.

func createAddress() -> Address {
	print("Function was called.")

	let someAddress = Address()
	someAddress.buildingNumber = "29"
	someAddress.street = "Acacia Road"

	return someAddress
}
john.residence?.address = createAddress()

 

You can tell that the createAddress() function isn’t called, because nothing is printed.

너는 createAddress() 함수가 호출되지 않은걸 알수 있다, 왜냐면 아무것도 출력되지 않았으니까.

 

Calling Methods Through Optional Chaining

You can use optional chaining to call a method on an optional value, and to check whether that method call is successful. You can do this even if that method does not define a return value.

옵셔널 값에서 메소드를 호출할때 옵셔널 체이닝을 이용할수 있고, 그 메소드의 호출이 성공했는지 검사할수 있다.

비록 그 메소드가 리턴값을 정의하지 않았더라도 옵셔널 체이닝을 사용할수 있다.

 

The printNumberOfRooms() method on the Residence class prints the current value of numberOfRooms. Here’s how the method looks:

Residence 클래스의 printNumberOfRooms() 메소드는 numberOfRooms 의 현재 값을 출력한다.

func printNumberOfRooms() {
	print("The number of rooms is \(numberOfRooms)")
}

 

This method does not specify a return type. However, functions and methods with no return type have an implicit return type of Void, as described in Functions Without Return Values. This means that they return a value of (), or an empty tuple.

이 메소드는 리턴 값을 명시하지 않았다, 리턴타입이 없는 함수와 메소드는 암시적인 리턴타입 Void 를 가진다.

이것은 리턴 값은 () 또는 빈 튜플이라는 뜻이다.

 

If you call this method on an optional value with optional chaining, the method’s return type will be Void?, not Void, because return values are always of an optional type when called through optional chaining. This enables you to use an if statement to check whether it was possible to call the printNumberOfRooms() method, even though the method does not itself define a return value. Compare the return value from the printNumberOfRooms call against nil to see if the method call was successful:

너가 옵셔널 값에서 옵셔널 체이닝으로 이 메소드를 호출하면, 그 메소드는 Void? 타입을 리턴한다. Void 가 아니다.

왜냐면 옵셔널 체이닝을 통해서 호출될때 리턴 값은 항상 옵셔널 값이기 때문이다.

너는 이 메소드가 리턴값을 정의하지 않았더라고, if 문을 이용해서 printNumberOfRooms() 메소드 호출이 가능한지 검사할수 있다.

printNumberOfRooms 호출의 리턴 값을 nil 과 비교해서 메소드 호출이 성공했는지 알수 있다.

if john.residence?.printNumberOfRooms() != nil {
	print("It was possible to print the number of rooms.")
} else {
	print("It was not possible to print the number of rooms.")
}
// Prints "It was not possible to print the number of rooms."

 

The same is true if you attempt to set a property through optional chaining. The example above in Accessing Properties Through Optional Chaining attempts to set an address value for john.residence, even though the residence property is nil. Any attempt to set a property through optional chaining returns a value of type Void?, which enables you to compare against nil to see if the property was set successfully:

옵셔널 체이닝을 통해서 속성을 설정하려고 시도할때도 위의 사실들이 동일하게 적용된다.

위의 예제에서 비록 residence 속성이 nil 이더라도, john.residence 에 address 값을 설정하려고 시도한다.

옵셔널 체이닝을 통해서 속성을 설정하려는 모든 시도는 Void? 타입을 리턴해서,

nil 과 비교하면 속성 설정이 성공했는지 알수 있다.

if (john.residence?.address = someAddress) != nil {
	print("It was possible to set the address.")
} else {
	print("It was not possible to set the address.")
}
// Prints "It was not possible to set the address."

 

Accessing Subscripts Through Optional Chaining

You can use optional chaining to try to retrieve and set a value from a subscript on an optional value, and to check whether that subscript call is successful.

옵셔널 값을 가지는 첨자에 값을 조회하거나 설정할때 옵셔널 체이닝을 이용할수 있고,

첨자 호출이 성공했는지 여부를 검사할수 있다.

 

NOTE

When you access a subscript on an optional value through optional chaining, you place the question mark before the subscript’s brackets, not after. The optional chaining question mark always follows immediately after the part of the expression that is optional.

옵셔널 값을 가지는 첨자에 옵셔널 체이닝을 통해서 접근할때, 물음표는 첨자의 괄호 앞에 놓아야 한다. 뒤가 아니다.

옵셔널 체이닝 물음표 마크는 항상 옵셔널 표현식의 바로 뒤에 온다.

 

The example below tries to retrieve the name of the first room in the rooms array of the john.residence property using the subscript defined on the Residence class. Because john.residence is currently nil, the subscript call fails:

아래 예제는 Residence 클래스에 정의된 첨자를 이용해서

john.residence 속성의 rooms 배열에 있는 첫번째 방의 이름을 조회하려고 시도한다.

john.residence 가 현재 nil 이라서, 첨자 호출은 실패한다.

if let firstRoomName = john.residence?[0].name {
	print("The first room name is \(firstRoomName).")
} else {
	print("Unable to retrieve the first room name.")
}
// Prints "Unable to retrieve the first room name."

 

The optional chaining question mark in this subscript call is placed immediately after john.residence, before the subscript brackets, because john.residence is the optional value on which optional chaining is being attempted.

Similarly, you can try to set a new value through a subscript with optional chaining:

이 첨자 호출에서 옵셔널 체이닝 물음표는 john.residence 바로 뒤에 위치한다, 첨자 괄호 바로 앞에,

왜냐면 john.residence 가 옵셔널 체이닝이 시도되는 옵셔널 값이기 때문이다.

비슷하게, 첨자에 옵셔널 체이닝을 통해서 새로운 값을 설정하려고 시도 할수 있다.

john.residence?[0] = Room(name: "Bathroom")

 

This subscript setting attempt also fails, because residence is currently nil.

현재 residence가 nil 이라서, 이 첨자 설정은 실패할것이다,

 

If you create and assign an actual Residence instance to john.residence, with one or more Room instances in its rooms array, you can use the Residence subscript to access the actual items in the rooms array through optional chaining:

너가 실제로 Residence를 만들고 john.residence 에 할당하면, 하나 이상의 Room 인스턴스가 rooms 배열에 들어가고,

너는 옵셔널 체이닝을 통해서 rooms 배열에 있는 실제 item들에 접근하는 Residence 첨자를 사용할수 있다.

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
	print("The first room name is \(firstRoomName).")
} else {
	print("Unable to retrieve the first room name.")
}
// Prints "The first room name is Living Room."

 

Accessing Subscripts of Optional Type

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:

첨자는 옵셔널 타입의 값을 리턴한다 -swift Dictionary 타입의 key 첨자처럼-

물음표를 첨자의 닫기 괄호 뒤에 놓아라.

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]

 

The example above defines a dictionary called testScores, which contains two key-value pairs that map a String key to an array of Int values. The example uses optional chaining to set the first item in the "Dave" array to 91; to increment the first item in the "Bev" array by 1; and to try to set the first item in an array for a key of "Brian". The first two calls succeed, because the testScores dictionary contains keys for "Dave" and "Bev". The third call fails, because the testScores dictionary does not contain a key for "Brian".

위의 예에서 testScores 라는 dictionary를 정의해서, 2개의 key(String)-value(Int 배열) 쌍을 포함한다.

이 예제는 옵셔널 체이닝을 이용해서 'Dave' 배열의 첫번째 아이템에 91을 설정한다.

'Bev' 배열의 첫번째 아이템에 1을 증가시킨다.

'Brian' 키에 해당하는 배열의 첫번째 아이템에 값을 설정하려고 시도한다.

testScores dictionary는 'Dave', 'Bev' 라는 키를 가지고 있기 때문에, 위에 2개 호출은 성공한다.

testScores dictionary는 'Brian' 이라는 키를 가지고 있지 않기 때문에, 세번째 호출은 실패한다.

 

Linking Multiple Levels of Chaining

You can link together multiple levels of optional chaining to drill down to properties, methods, and subscripts deeper within a model. However, multiple levels of optional chaining do not add more levels of optionality to the returned value.

하나의 모델에서 옵셔널 체이닝의 여러 레벨를 한데 묶어서 속성, 메소드, 첨자로 드릴다운 할수 있다.

그러나 옵셔널 체이닝의 여러 레벨이 리턴 값에 대해서는 더 많은 레벨을 추가하지 않는다

 

To put it another way:

다시 말하면:

  • If the type you are trying to retrieve is not optional, it will become optional because of the optional chaining.
  • If the type you are trying to retrieve is already optional, it will not become more optional because of the chaining.
  • 조회하려는 타입이 옵셔널이 아니면, 옵셔널 체이닝에 의해서 옵셔널이 된다.
  • 조회하려는 타입이 이미 옵셔널이면, 체이닝에 의해서 더 옵셔널이 되지는 않는다.

 

Therefore:

그러므로:

  • If you try to retrieve an Int value through optional chaining, an Int? is always returned, no matter how many levels of chaining are used.
  • Similarly, if you try to retrieve an Int? value through optional chaining, an Int? is always returned, no matter how many levels of chaining are used.
  • 옵셔널 체이닝을 통해서 Int 값을 조회할때는, 항상 Int? 가 리턴된다. 얼마나 많은 레벨이 체이닝에 사용되었는지는 상관없다.
  • 비슷하게, 옵셔널 체이닝을 통해서 Int? 값을 조회할때는, 항상 Int? 가 리턴된다. 얼마나 많은 레벨이 체이닝에 사용되었는지는 상관없다.

 

The example below tries to access the street property of the address property of the residence property of john. There are two levels of optional chaining in use here, to chain through the residence and address properties, both of which are of optional type:

아래 예제는 john의 residence 속성의 address 속성의 street 속성에 접근하려고 시도한다.

여기에는 2단계의 옵셔널 체이닝이 사용되었고, 둘다 옵셔널 타입인 residence 와 address 속성을 연결했다.

if let johnsStreet = john.residence?.address?.street {
	print("John's street name is \(johnsStreet).")
} else {
	print("Unable to retrieve the address.")
}
// Prints "Unable to retrieve the address."

 

The value of john.residence currently contains a valid Residence instance. However, the value of john.residence.address is currently nil. Because of this, the call to john.residence?.address?.street fails.

john.residence의 값은 현재 올바른 Residence 인스턴스를 포함하고 있다.

그러나 john.residence.address 는 현재 nil 이다.

이런 이유로, john.residence?.address?.street 호출은 실패한다.

 

Note that in the example above, you are trying to retrieve the value of the street property. The type of this property is String?. The return value of john.residence?.address?.street is therefore also String?, even though two levels of optional chaining are applied in addition to the underlying optional type of the property.

위의 예제에서, street 속성의 값을 조회하려고 시도한다는걸 주목해라. 이 속성의 타입은 String? 이다.

john.residence?.address?.street 의 리턴값은 그러므로 String? 이다.

비록 2단계의 옵셔널 체이닝이 그 속성의 기본 옵셔널 타입에 추가로 적용된다고 하더라도 (???)

(이석우 추가 : 그냥 옵셔널 체이닝이 옵셔널을 리턴하긴 하지만, street 타입 자체가 옵셔널이라서 String? 을 리턴한다는 말인거 같다)

 

If you set an actual Address instance as the value for john.residence.address, and set an actual value for the address’s street property, you can access the value of the street property through multi level optional chaining:

john.residence.address 에 실제로 Address 인스턴스를 설정한다면, 그리고 address의 street 속성에 실제 값을 설정한다면,

너는 다중 레벨의 옵셔널 체이닝을 통해서 street 속성의 값에 접근할수 있다.

let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress

if let johnsStreet = john.residence?.address?.street {
	print("John's street name is \(johnsStreet).")
} else {
	print("Unable to retrieve the address.")
}
// Prints "John's street name is Laurel Street."

 

In this example, the attempt to set the address property of john.residence will succeed, because the value of john.residence currently contains a valid Residence instance.

이 예제에서, john.residence 의 address 속성에 설정하려는 시도는 성공한다.

왜냐면 john.residence 는 현재 올바른 Residence 인스턴스를 가지고 있으니까.

 

Chaining on Methods with Optional Return Values

The previous example shows how to retrieve the value of a property of optional type through optional chaining. You can also use optional chaining to call a method that returns a value of optional type, and to chain on that method’s return value if needed.

이전의 예제는 옵셔널 체이닝을 통해서 옵셔널 타입인 속성의 값을 조회하는 법을 보여준다.

또한 옵셔널 타입의 값을 리턴하는 메소드를 호출할때에도 옵셔널 체이닝을 사용할수 있다. (그 메소드의 리턴값이 필요하다면)

 

The example below calls the Address class’s buildingIdentifier() method through optional chaining. This method returns a value of type String?. As described above, the ultimate return type of this method call after optional chaining is also String?:

아래 예제는 Address 클래스의 buildingIdentifier() 메소드를 옵셔널 체이닝을 통해서 호출한다.

이 메소드는 String? 타입의 값을 리턴한다.

위에서 보았듯이, 옵셔널 체이닝 이후 메소드 호출의 최종 리턴 타입은 역시 String? 이다

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
	print("John's building identifier is \(buildingIdentifier).")
}
// Prints "John's building identifier is The Larches."

 

If you want to perform further optional chaining on this method’s return value, place the optional chaining question mark after the method’s parentheses:

이 메소드의 리턴 값에 대해서 더 많은 옵셔널 체이닝을 수행하려면,

그 메소드의 괄호 뒤에 옵셔널 체이닝 물음표를 놓아라.

if let beginsWithThe =
	john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
	if beginsWithThe {
		print("John's building identifier begins with \"The\".")
	} else {
		print("John's building identifier does not begin with \"The\".")
	}
}
// Prints "John's building identifier begins with "The"."

 

NOTE

In the example above, you place the optional chaining question mark after the parentheses, because the optional value you are chaining on is the buildingIdentifier() method’s return value, and not the buildingIdentifier() method itself.

위의 예제에서, 옵셔널 체이닝 물음표를 메소드의 괄호 뒤에 놓았다.

왜냐면 너가 묶으려는 옵셔널 값은 buildingIdentifier() 메소드 자체가 아니고, buildingIdentifier() 메소드의 리턴 값이기 때문이다.

반응형

'iOS 초보' 카테고리의 다른 글

[swift5.1번역] 18.Type Casting  (0) 2019.11.25
[swift5.1번역] 17.Error Handling  (0) 2019.11.14
[swift5.1번역] 15.Deinitialization  (0) 2019.09.17
[swift5.1번역] 14.Initialization  (0) 2019.09.04
[swift5.1번역] 13.Inheritance  (0) 2019.08.27
Posted by 돌비
,