* 내가 읽으려고 내 맘대로 번역한 글.

* 원문 : https://docs.swift.org/swift-book/LanguageGuide/Properties.html

 

 

Properties

Properties associate values with a particular class, structure, or enumeration. Stored properties store constant and variable values as part of an instance, whereas computed properties calculate (rather than store) a value. Computed properties are provided by classes, structures, and enumerations. Stored properties are provided only by classes and structures.

Stored and computed properties are usually associated with instances of a particular type. However, properties can also be associated with the type itself. Such properties are known as type properties.

In addition, you can define property observers to monitor changes in a property’s value, which you can respond to with custom actions. Property observers can be added to stored properties you define yourself, and also to properties that a subclass inherits from its superclass.

속성은 특정 클래스, 구조체, 열거형과 연관된 값이다.

저장 속성은 인스턴스의 일부로써 상수값이나 변수값를 저장하나,

계산된 속성들은 값을 저장하는 대신 계산한다.

계산된 속성은 클래스, 구조체, 열거형에서 사용가능하다.

저장된 속성은 클래스, 구조체에 의해서만 사용가능하다.

저장된 속성과 계산된 속성은 일반적으로 특정 타입의 인스턴스와 연관되어 있다.

속성은 또한 타입 자체와 연관되어 있을수도 있다. 이런 속성을 타입 속성이라고 한다.

게다가 속성의 값이 변경되는지 모니터링하기 위해서 속성 observer (감시자)를 정의하여 너가 특정행동을 할수 있다.

속성 observer는 너가 정의한 저장 속성에 추가될수 있고, 부모클래스에서 상속받은 서브클래스의 속성에도 추가될수 있다.

 

Stored Properties

In its simplest form, a stored property is a constant or variable that is stored as part of an instance of a particular class or structure. Stored properties can be either variable stored properties (introduced by the var keyword) or constant stored properties (introduced by the let keyword).

가장 간단한 형태로, 저장된 속성은 특정한 구조체나 클래스의 인스턴스의 일부로써 상수나 변수다.

저장된 속성은 var 키워드로 변수가 될수 있고, let 키워드로 상수가 될수 있다.

 

You can provide a default value for a stored property as part of its definition, as described in Default Property Values. You can also set and modify the initial value for a stored property during initialization. This is true even for constant stored properties, as described in Assigning Constant Properties During Initialization.

저장된 속성은 정의할때 기본값을 넣을수 있다. 또한 초기화 과정에서 초기값을 설정하거나 수정할수도 있다.

이건 상수에 대해서도 마찬가지다.

 

The example below defines a structure called FixedLengthRange, which describes a range of integers whose range length cannot be changed after it is created:

아래 예제는 FixedLengthRange라는 구조체를 정의한다. 이것은 처음 생성된후에 변경할수 없는 범위을 설명한다.

struct FixedLengthRange {
	var firstValue: Int
	let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8

 

Instances of FixedLengthRange have a variable stored property called firstValue and a constant stored property called length. In the example above, length is initialized when the new range is created and cannot be changed thereafter, because it is a constant property.

FixedLengthRange의 인스턴스는 firstValue 변수와 length 상수를 가지고 있다.

위의 예에서, length는 새로운 범위가 생성될때 초기화 되고, 나중에는 바꿀수 없다.

왜냐면 상수니까.

 

Stored Properties of Constant Structure Instances

If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties:

너가 구조체의 인스턴스를 만들고 상수에 할당하면, 그 인스턴스의 속성을 바꿀수 없다.

비록 그 속성이 변수로 선언되어 있더라도.

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property

 

Because rangeOfFourItems is declared as a constant (with the let keyword), it is not possible to change its firstValue property, even though firstValue is a variable property.

This behavior is due to structures being value types. When an instance of a value type is marked as a constant, so are all of its properties.

The same is not true for classes, which are reference types. If you assign an instance of a reference type to a constant, you can still change that instance’s variable properties.

rangeOfFourItems가 상수로(let 키워드) 선언되었기 때문에, firstValue 속성이 변수이더라도 firstValue 속성을 바꿀수 없다.

이것은 구조체가 값타입이기 때문이다. 값타입의 인스턴스가 상수이면, 그 인스턴스가 가지고 있는 모든 속성도 상수가 된다.

클래스는 참조타입이기 때문에 그렇지 않다.

너가 참조타입의 인스턴스를 상수에 할당해도, 그 인스턴스의 변수들은 변할수 있다.

 

Lazy Stored Properties

A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

lazy (지연된) 저장 속성은 처음 사용되기 전까지는 초기값이 계산되지 않는 속성이다.

속성을 정의하기 전에 lazy 키워드를 적어서 지연된 저장 속성임을 표시한다.

 

NOTE

You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.

lazy 속성은 반드시 var 키워드를 이용하여 변수로 선언해야 한다.

왜냐면 이것의 초기값은 인스턴스 초기화가 완료되기 전까지 가져올수 없으니까.

상수는 항상 초기화가 완료되기 전에 값을 가지고 있어야 하니까 lazy 로 선언될수 없다.

 

Lazy properties are useful when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete. Lazy properties are also useful when the initial value for a property requires complex or computationally expensive setup that should not be performed unless or until it is needed.

The example below uses a lazy stored property to avoid unnecessary initialization of a complex class. This example defines two classes called DataImporter and DataManager, neither of which is shown in full:

lazy 속성은 초기값이 인스턴스의 초기화가 완료되기 전에는 알수 없는 외부의 요소에 영향을 받을때 유용하다.

또한 lazy 속성은 초기값이 복잡하거나 실제로 필요하기 전에는 비싼 계산비용을 수행할 필요가 없는 경우 유용하다.

아래의 예제는 복잡한 클래스의 불필요한 초기화를 피하기 위해서 lazy 저장 속성을 사용한다.

DataImporter 와 DataManager라는 클래스를 정의한다.

class DataImporter {
	/*
	DataImporter is a class to import data from an external file.
	The class is assumed to take a nontrivial amount of time to initialize.
	*/
	var filename = "data.txt"
	// the DataImporter class would provide data importing functionality here
}

class DataManager {
	lazy var importer = DataImporter()
	var data = [String]()
	// the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property has not yet been created

 

The DataManager class has a stored property called data, which is initialized with a new, empty array of String values. Although the rest of its functionality is not shown, the purpose of this DataManager class is to manage and provide access to this array of String data.

DataManager 클래스는 data 라는 저장된 속성을 가지고 있고 빈 문자열 배열로 초기화  되었다.

나머지 기능들이 보이지 않더라도, DataManager 클래스의 목적은 문자열 배열 데이타를 관리하고 접근을 제공하는 것이다.

 

Part of the functionality of the DataManager class is the ability to import data from a file. This functionality is provided by the DataImporter class, which is assumed to take a nontrivial amount of time to initialize. This might be because a DataImporter instance needs to open a file and read its contents into memory when the DataImporter instance is initialized.

DataManager 클래스의 기능중 일부는 파일에서 데이타를 가져오는 것이다.

그 기능은 DataImporter 클래스에 의해서 제공되는데, 초기화하는데 많은 시간이 걸린다.

DataImporter 인스턴스가 초기화될때 파일을 열고, 읽고 그 내용을 메모리에 저장하는게 필요하기 때문이다.

 

It is possible for a DataManager instance to manage its data without ever importing data from a file, so there is no need to create a new DataImporter instance when the DataManager itself is created. Instead, it makes more sense to create the DataImporter instance if and when it is first used.

DataManager 인스턴스가 파일에서 데이타를 불러들이지 않고 데이타를 관리할수 있으니까,

DataManager가 생성될때 DataImporter 인스턴스를 만들필요는 없다.

대신 DataImporter 인스턴스가 처음 사용될때 만드는게 더 합리적이다.

 

Because it is marked with the lazy modifier, the DataImporter instance for the importer property is only created when the importer property is first accessed, such as when its filename property is queried:

lazy 키워드가 붙었기 때문에, importer 속성에 대한 DataImporter 인스턴스는 importer 속성에 처음 접근할때 생성된다.

filename 속성이 질의될때처럼.

print(manager.importer.filename)
// the DataImporter instance for the importer property has now been created
// Prints "data.txt"

 

NOTE

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

만약 lazy가 붙은 속성이 다중 쓰레드에서 동시에 접근되고 그 속성이 아직 초기화되지 않았다면,

그 속성이 오직 한번만 초기화 된다는 보장은 없다.

 

Stored Properties and Instance Variables

If you have experience with Objective-C, you may know that it provides two ways to store values and references as part of a class instance. In addition to properties, you can use instance variables as a backing store for the values stored in a property.

너가 objective-c 경험이 있다면, 클래스 인스턴의 일부로써 값과 참조를 저장하는 2개의 방식이 있다는걸 알거다.

속성과 더불어, 너는 인스턴스 변수를 속성의 백업 저장소로 사용할수 있다.

(이석우 추가 : 속성이름 앞에 _ 가 붙은 인스턴스 변수를 말하는거 같음)

 

Swift unifies these concepts into a single property declaration. A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. This approach avoids confusion about how the value is accessed in different contexts and simplifies the property’s declaration into a single, definitive statement. All information about the property—including its name, type, and memory management characteristics—is defined in a single location as part of the type’s definition.

swift는 이러한 개념들을 하나의 속성 선언으로 통합하였다.

swift 속성은 일치하는 인스턴스 변수를 가지지 않고, 속성에 대한 백업 저장소에 직접 접근할수 없다.

이러한 방식은 다른 접근방식에 대한 혼란을 피할수 있고, 속성의 선언을 하나의 확실한 문장으로 단순화 시켜준다.

속성에 대한 모든 정보 (이름, 타입, 메모리 관리 특성을 포함한)는 타입 선언의 일부로써 한곳에서 선언된다.

 

Computed Properties

In addition to stored properties, classes, structures, and enumerations can define computed properties, which do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.

저장된 속성외에, 클래스, 구조체, 열거형은 실제로 값을 저장하지 않는 계산된 속성을 정의할수 있다.

대신 getter와 optional setter를 제공해서 간접적으로 다른 속성의 값을 가져오거나 설정하는데 사용된다.

struct Point {
	var x = 0.0, y = 0.0
}

struct Size {
	var width = 0.0, height = 0.0
}

struct Rect {
	var origin = Point()
	var size = Size()
	var center: Point {
		get {
			let centerX = origin.x + (size.width / 2)
			let centerY = origin.y + (size.height / 2)
			return Point(x: centerX, y: centerY)
		}
		set(newCenter) {
			origin.x = newCenter.x - (size.width / 2)
			origin.y = newCenter.y - (size.height / 2)
		}
	}
}

var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"

 

This example defines three structures for working with geometric shapes:

  • Point encapsulates the x- and y-coordinate of a point.
  • Size encapsulates a width and a height.
  • Rect defines a rectangle by an origin point and a size.

 

The Rect structure also provides a computed property called center. The current center position of a Rect can always be determined from its origin and size, and so you don’t need to store the center point as an explicit Point value. Instead, Rect defines a custom getter and setter for a computed variable called center, to enable you to work with the rectangle’s center as if it were a real stored property.

Rect 구조체는 center 라는 계산된 속성을 제공한다. Rect 의 현재 center 위치는 origin과 size로 부터 결정되고,

center 위치를 저장할 필요는 없다. 대신 Rect 은 getter와 setter를 정의해서 center 변수를 계산해서

마치 실제로 저장된 속성인것처럼 동작하게 해준다.

 

The example above creates a new Rect variable called square. The square variable is initialized with an origin point of (0, 0), and a width and height of 10. This square is represented by the blue square in the diagram below.

위의 예제는 square 라는 변수를 만들었다. square 변수는 위치 (0, 0) 과 크기 (10, 10) 으로 초기화 되었다.

이 square 는 아래 그림에서 파란색으로 표시된다.

 

The square variable’s center property is then accessed through dot syntax (square.center), which causes the getter for center to be called, to retrieve the current property value. Rather than returning an existing value, the getter actually calculates and returns a new Point to represent the center of the square. As can be seen above, the getter correctly returns a center point of (5, 5).

square 변수의 center 속성은 점 문법을 통해서 접근되고 (square.center) getter 가 호출되어 현재 속성의 값을 조회한다.

기존값을 리턴하는 대신, getter 는 실제로 계산하고 새로운 Point 를 리턴해서 square의 center를 표현한다.

위에서 보여지듯이, getter는 정확히 (5, 5) 를 리턴한다.

 

The center property is then set to a new value of (15, 15), which moves the square up and to the right, to the new position shown by the orange square in the diagram below. Setting the center property calls the setter for center, which modifies the x and y values of the stored origin property, and moves the square to its new position.

center 속성이 새로운 값 (15, 15)으로 설정되었고, square 는 오른쪽위로 이동한다.

아래 그림의 오렌지색 square. center 속성의 설정은 setter 를 호출하여, x, y 값을 수정하여 origin 속성에 저장하고,

square를 새로운 위치로 이동시킨다.

(이석우 추가 : 아래 그림에서 새로운 좌표 15, 15 는 잘못되었음. 10, 10 이어야 함)

 

Shorthand Setter Declaration

If a computed property’s setter doesn’t define a name for the new value to be set, a default name of newValue is used. Here’s an alternative version of the Rect structure that takes advantage of this shorthand notation:

계산된 속성의 setter에 이름을 정하지 않으면, 기본 이름 newValue가 사용된다.

여기 간략한 Rect 구조체의 다른 버젼이 있다.

struct AlternativeRect {
	var origin = Point()
	var size = Size()
	var center: Point {
		get {
			let centerX = origin.x + (size.width / 2)
			let centerY = origin.y + (size.height / 2)
			return Point(x: centerX, y: centerY)
		}
		set {
			origin.x = newValue.x - (size.width / 2)
			origin.y = newValue.y - (size.height / 2)
		}
	}
}

 

Shorthand Getter Declaration

If the entire body of a getter is a single expression, the getter implicitly returns that expression. Here’s an another version of the Rect structure that takes advantage of this shorthand notation and the shorthand notation for setters:

getter가 하나의 표현으로만 구성된다면, getter 는 암시적으로 그 표현을 리턴한다.

여기 getter와 setter를 간략화 한 Rect 구조체의 다른 버젼이 있다

struct CompactRect {
	var origin = Point()
	var size = Size()
	var center: Point {
		get {
			Point(x: origin.x + (size.width / 2), y: origin.y + (size.height / 2))
		}
		set {
			origin.x = newValue.x - (size.width / 2)
			origin.y = newValue.y - (size.height / 2)
		}
	}
}

 

Omitting the return from a getter follows the same rules as omitting return from a function, as described in Functions With an Implicit Return.

getter 에서 return을 생략하는것은 함수에서 return을 생략하는것과 같은 규칙을 따른다.

 

Read-Only Computed Properties

A computed property with a getter but no setter is known as a read-only computed property. A read-only computed property always returns a value, and can be accessed through dot syntax, but cannot be set to a different value.

getter만 있고 setter는 없는 계산된 속성은 read-only 속성이다.

read-only 계산된 속성은 항상 값을 리턴하고 . 문법으로 접근 가능하지만, 다른 값으로 설정할수는 없다.

 

NOTE

You must declare computed properties—including read-only computed properties—as variable properties with the var keyword, because their value is not fixed. The let keyword is only used for constant properties, to indicate that their values cannot be changed once they are set as part of instance initialization.

계산된 속성은 (read-only 를 포함해서) 반드시 var 키워드를 이용해서 변수로 정의해야 한다.

왜냐면 그 값은 정해진게 아니니까.

let 키워드는 상수 속성에만 사용해서 그 값이 인스턴스의 초기화되면 바뀌지 않는다는걸 표시해야 한다.

 

You can simplify the declaration of a read-only computed property by removing the get keyword and its braces:

read-only 계산된 속성을 get 키워드 빼고 간단하게 선언할수 있다.

struct Cuboid {
	var width = 0.0, height = 0.0, depth = 0.0
	var volume: Double {
		return width * height * depth
	}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// Prints "the volume of fourByFiveByTwo is 40.0"

 

This example defines a new structure called Cuboid, which represents a 3D rectangular box with width, height, and depth properties. This structure also has a read-only computed property called volume, which calculates and returns the current volume of the cuboid. It doesn’t make sense for volume to be settable, because it would be ambiguous as to which values of width, height, and depth should be used for a particular volume value. Nonetheless, it is useful for a Cuboid to provide a read-only computed property to enable external users to discover its current calculated volume.

이 예제는 넓이, 높이, 깊이 속성의 3차원 사각형을 표현하는 Cuboid 라는 새로운 구조체를 정의한다.

이 구조체는 volumne 이라는 read-only 계산된 속성을 가지고 있고, cuboid의 현재 볼륨을 계산하고 리턴한다.

volume 에 값을 설정하는것은 말이 안된다, 왜냐면 볼륨 값에 넓이, 높이, 깊이중 어떤게 사용되지는 모호해지기 때문이다.

하지만 외부 사용자가 현재 계산된 볼륨을 알기위해서 read-only 계산된 속성을 제공하는것은 유용하다.

Property Observers

Property observers observe and respond to changes in a property’s value. Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value.

속성 옵저버는 속성값의 변화를 관찰하고 응답한다.

속성 옵저버는 새로운 값이 기존값과 같을지라도 속성의 값이 설정될때마다 호출된다.

 

You can add property observers to any stored properties you define, except for lazy stored properties. You can also add property observers to any inherited property (whether stored or computed) by overriding the property within a subclass. You don’t need to define property observers for nonoverridden computed properties, because you can observe and respond to changes to their value in the computed property’s setter. Property overriding is described in Overriding.

너가 정의한 모든 저장 속성에 속성 옵저버를 넣을수 있다. 단 lazy 저장 속성 빼고.

또한 상속받은 속성 (저장 속성 또는 계산된 속성 모두)을 서브클래스에서 overriding 했다면 속성 옵저버를 추가할수 있다.

override 되지 않은 계산된 속성에는 속성 옵저버를 정의할 필요가 없다.

왜냐면 계산된 속성의 setter 안에서 사용하는 값들을 옵저버 하면 되니까.

 

You have the option to define either or both of these observers on a property:

너는 속성에 아래에 있는 것들중 하나 이상을 정의할수 있다.

  • willSet is called just before the value is stored.
  • didSet is called immediately after the new value is stored.
  • willSet 은 값이 저장되기 직전에 호출된다.
  • didSet 은 새로운 값이 저장된 직후 호출된다.

If you implement a willSet observer, it’s passed the new property value as a constant parameter. You can specify a name for this parameter as part of your willSet implementation. If you don’t write the parameter name and parentheses within your implementation, the parameter is made available with a default parameter name of newValue.

너가 willSet 옵저버를 구현하면 새로운 속성값이 상수 매개변수로 전달된다. willSet 을 구현할때 그 매개변수 이름을 정할수 있다.

구현할때 매개변수 이름과 괄호를 쓰지 않으면, 기본 이름 newValue로 사용 가능하다.

 

Similarly, if you implement a didSet observer, it’s passed a constant parameter containing the old property value. You can name the parameter or use the default parameter name of oldValue. If you assign a value to a property within its own didSet observer, the new value that you assign replaces the one that was just set.

비슷하게 didSet 옵저버를 구현할때, 속성의 이전 값이 상수 매개변수로 전달된다. 너는 매개변수 이름을 정할수 있고,

기본 매개변수 이름인 oldValue를 사용할수도 있다.

너가 didSet 옵저버 안에서 속성에 값을 할당하면, 너가 할당한 새로운 값은 방금 설정된 값으로 바뀐다. (???)

 

NOTE

The willSet and didSet observers of superclass properties are called when a property is set in a subclass initializer, after the superclass initializer has been called. They are not called while a class is setting its own properties, before the superclass initializer has been called.

For more information about initializer delegation, see Initializer Delegation for Value Types and Initializer Delegation for Class Types.

부모클래스 속성의 willSet 과 didSet 옵저버는 부모클래스의 초기화가 끝난후, 서브클래스 초기화에서 속성이 설정될때도 호출된다.

부모클래스 초기화가 호출되기 전에, 클래스가 자기 자신의 속성을 설정할때는 호출되지 않는다.

 

Here’s an example of willSet and didSet in action. The example below defines a new class called StepCounter, which tracks the total number of steps that a person takes while walking. This class might be used with input data from a pedometer or other step counter to keep track of a person’s exercise during their daily routine.

여기 willSet 과 didSet 의 작동 예가 있다.

StepCounter 라는 새로운 클래스를 정의했고, 사람이 걷는 총 걸음수를 추적한다.

이 클래스는 만보계나 다른 걸음 계산기로부터 데이타를 입력받아 일상생활에서 사람들의 운동을 추적하는데 사용할수 있다.

class StepCounter {
	var totalSteps: Int = 0 {
		willSet(newTotalSteps) {
			print("About to set totalSteps to \(newTotalSteps)")
		}
		didSet {
			if totalSteps > oldValue {
				print("Added \(totalSteps - oldValue) steps")
			}
		}
	}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

 

The StepCounter class declares a totalSteps property of type Int. This is a stored property with willSet and didSet observers.

The willSet and didSet observers for totalSteps are called whenever the property is assigned a new value. This is true even if the new value is the same as the current value.

This example’s willSet observer uses a custom parameter name of newTotalSteps for the upcoming new value. In this example, it simply prints out the value that is about to be set.

The didSet observer is called after the value of totalSteps is updated. It compares the new value of totalSteps against the old value. If the total number of steps has increased, a message is printed to indicate how many new steps have been taken. The didSet observer does not provide a custom parameter name for the old value, and the default name of oldValue is used instead.

StepCounter 클래스는 totalSteps 속성을 Int 로 정의하였고, willSet과 didSet 옵저버가 추가된 저장 속성이다.

totalSteps 의 willSet과 didSet은 새로운 값이 할당될때마다 호출된다.

새로운 값이 기존 값과 동일하더라도 호출된다.

이 예에서 willSet 옵저버는 매개변수 이름을 newTotalSteps 라고 지었고, 단순히 방금 설정된 값을 출력한다.

didSet 옵저버는 totalSteps 값이 변경된 후에 호출되고, 새로운 totalSteps 값과 이전 값을 비교한다.

총 걸음수가 증가했다면, 얼만큼 증가했는지 메세지를 출력한다.

didSet 옵저버는 매개변수 이름을 따로 쓰지 않아서, 기본 이름인 oldValue 가 사용되었다.

 

NOTE

If you pass a property that has observers to a function as an in-out parameter, the willSet and didSet observers are always called. This is because of the copy-in copy-out memory model for in-out parameters: The value is always written back to the property at the end of the function. For a detailed discussion of the behavior of in-out parameters, see In-Out Parameters.

옵저버를 가진 속성을 다른 함수에 in-out 매개변수로 넘기면, willSet과 didSet 옵저버는 항상 호출된다.

이것은 in-out 매개변수에 대한 copy-in copy-out 메모리 모델 때문이다.

그 값은 함수가 끝나면 항상 다시 쓰여진다.

 

Global and Local Variables

The capabilities described above for computing and observing properties are also available to global variables and local variables. Global variables are variables that are defined outside of any function, method, closure, or type context. Local variables are variables that are defined within a function, method, or closure context.

위에서 설명한 속성을 계산하고 옵저버 하는 기능들은 전역변수와 지역변수에 모두 해당한다.

전역변수는 모든 함수, 메소드, 클로저, 타입 콘텍스트 밖에서 정의된 변수다.

지역변수는 함수, 메소드, 클로저 컨텍스트 안에서 정의된 변수다.

 

The global and local variables you have encountered in previous chapters have all been stored variables. Stored variables, like stored properties, provide storage for a value of a certain type and allow that value to be set and retrieved.

지금까지 보았던 전역/지역 변수들은 모두 값을 저장하고 있다.

변수는 속성처럼 특정 타입의 값을 저장하고 꺼낼수 있는 저장소를 제공한다.

 

However, you can also define computed variables and define observers for stored variables, in either a global or local scope. Computed variables calculate their value, rather than storing it, and they are written in the same way as computed properties.

전역/지역 모두 계산된 변수와 옵저버를 정의할수 있다.

계산된 변수는 값을 저장하는 대신 값을 계산하고, 계산된 속성과 같은 방식으로 쓰여진다.

 

NOTE

Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with the lazy modifier.

Local constants and variables are never computed lazily.

전역상수와 전역변수는 lazy 저장 속성처럼 항상 늦게 계산된다.

lazy 저장 속성과 다르게, 전역상수와 전역변수는 lazy 키워드를 붙일 필요가 없다.

 

Type Properties

Instance properties are properties that belong to an instance of a particular type. Every time you create a new instance of that type, it has its own set of property values, separate from any other instance.

인스턴스의 속성은 특정 타입의 인스턴스에 속한 속성이다.

너가 특정 타입의 인스턴스를 만들때마다, 다른 인스턴스와들 분리된 자신만의 속성 집합을 가진다.

 

You can also define properties that belong to the type itself, not to any one instance of that type. There will only ever be one copy of these properties, no matter how many instances of that type you create. These kinds of properties are called type properties.

타입의 인스턴스가 아닌, 타입 자체에 속한 속성도 정의할수 있다.

이런 속성들은 인스턴스가 아무리 많더라도 오직 하나의 복사본만 존재한다.

이런 종류의 속성을 타입 속성이라고 부른다.

 

Type properties are useful for defining values that are universal to all instances of a particular type, such as a constant property that all instances can use (like a static constant in C), or a variable property that stores a value that is global to all instances of that type (like a static variable in C).

타입 속성은 모든 인스턴스를 아우르는 값을 정의할때 유용하다.

모든 인스턴스가 사용하는 상수 속성처럼 (c 에서 static 상수 처럼)

또는 모든 인스턴스에 적용되는 변수 속성처럼 (c 에서 static 변수 처럼)

 

Stored type properties can be variables or constants. Computed type properties are always declared as variable properties, in the same way as computed instance properties.

저장 타입 속성은 변수나 상수가 될수 있다.

계산 타입 속성은 항상 변수여야 한다. 계산 인스턴스 속성처럼.

 

NOTE

Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.

Stored type properties are lazily initialized on their first access. They are guaranteed to be initialized only once, even when accessed by multiple threads simultaneously, and they do not need to be marked with the lazy modifier.

저장 인스턴스 속성과 다르게, 저장 타입 속성은 항상 기본값을 주어야 한다.

왜냐면 타입 자체는 저장 타입 속성에 값을 할당하는 초기화 과정이 없기 때문이다.

저장 타입 속성은 첫 접근에 따라 늦게 초기화된다.

비록 멀티 쓰레드에서 동시에 접근하더라도 오직 한번만 초기화 되고, lazy 키워드를 쓸 필요 없다.

 

Type Property Syntax

In C and Objective-C, you define static constants and variables associated with a type as global static variables. In Swift, however, type properties are written as part of the type’s definition, within the type’s outer curly braces, and each type property is explicitly scoped to the type it supports.

c 와 objective-c 에서는, 전역 static 변수로써 타입과 연관된 static 상수와 변수를 정의한다 (??? 무슨 말이지?)

swift에서 타입 속성은 타입 선언의 일부로써 타입의 바깥쪽 중괄호 안에 쓰여지고,

각 타입 속성은 명시적으로 지원되는 범위에 속한다.

 

You define type properties with the static keyword. For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation. The example below shows the syntax for stored and computed type properties:

타입 속성은 static 키워드로 정의한다.

클래스 타입의 계산된 타입 속성은 대신 class 키워드를 사용해서 서브클래스의 구현에서 override 할수 있도록 허용할수 있다.

(이건 좀 이상한 문법 같은데...)

아래 예제는 저장 타입 속성과 계산 타입 속성의 문법을 보여준다.

struct SomeStructure {
	static var storedTypeProperty = "Some value."
	static var computedTypeProperty: Int {
		return 1
	}
}
enum SomeEnumeration {
	static var storedTypeProperty = "Some value."
	static var computedTypeProperty: Int {
		return 6
	}
}
class SomeClass {
	static var storedTypeProperty = "Some value."
	static var computedTypeProperty: Int {
		return 27
	}
	class var overrideableComputedTypeProperty: Int {
		return 107
	}
}

 

NOTE

The computed type property examples above are for read-only computed type properties, but you can also define read-write computed type properties with the same syntax as for computed instance properties.

위의 계산 타입 속성은 read-only 다.

read-write 계산 타입 속성을 계산 인스턴스 속성과 같은 문법으로 정의할수 있다.

 

Querying and Setting Type Properties

Type properties are queried and set with dot syntax, just like instance properties. However, type properties are queried and set on the type, not on an instance of that type. For example:

타입 속성은 인스턴스 속성처럼 . 문법으로 조회되고 설정할수 있다.

하지만 타입 속성은 타입을 통해서 조회되고 설정한다 (타입의 인스턴스가 아니다)

print(SomeStructure.storedTypeProperty)
// Prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// Prints "6"
print(SomeClass.computedTypeProperty)
// Prints "27"

 

The examples that follow use two stored type properties as part of a structure that models an audio level meter for a number of audio channels. Each channel has an integer audio level between 0 and 10 inclusive.

아래의 예제는 여러 오디오 채널에 대한 레벨 메터를 모델링하는 구조체의 일부로써 2개의 저장 타입 속성을 사용한다.

각 채널은 0부터 10까지의 오디오 레벨 숫자를 가진다.

 

The figure below illustrates how two of these audio channels can be combined to model a stereo audio level meter. When a channel’s audio level is 0, none of the lights for that channel are lit. When the audio level is 10, all of the lights for that channel are lit. In this figure, the left channel has a current level of 9, and the right channel has a current level of 7:

아래 그림은 이 오디오 채널들이 어떻게 결합되어서 스테레오 오디오 레벨 메터를 모델링하는지 보여준다.

채널의 오디오 레벨이 0이면 그 채널의 표시등이 켜지지 않는다.

오디오 레벨이 10이면 그 채널의 표시등은 모두 켜진다.

왼쪽 채널은 레벨 9이고, 오른쪽 체널은 레벨 7이다.

 

The audio channels described above are represented by instances of the AudioChannel structure:

위의 오디오 채널들은 AudioChannel 구조체의 인스턴스로 표현된다.

struct AudioChannel {
	static let thresholdLevel = 10
	static var maxInputLevelForAllChannels = 0
	var currentLevel: Int = 0 {
		didSet {
			if currentLevel > AudioChannel.thresholdLevel {
				// cap the new audio level to the threshold level
				currentLevel = AudioChannel.thresholdLevel
			}
			if currentLevel > AudioChannel.maxInputLevelForAllChannels {
			// store this as the new overall maximum input level
			AudioChannel.maxInputLevelForAllChannels = currentLevel
			}
		}
	}
}

 

The AudioChannel structure defines two stored type properties to support its functionality. The first, thresholdLevel, defines the maximum threshold value an audio level can take. This is a constant value of 10 for all AudioChannel instances. If an audio signal comes in with a higher value than 10, it will be capped to this threshold value (as described below).

AudioChannel 구조체는 기능상 2개의 저장 타입 속성을 정의한다.

첫번째 thresholdLevel 은 오디오 레벨이 취할수 있는 최대 임계값을 정의한다.

이것은 모든 AudioChannel 인스턴스가 10 이라는 값을 가진다.

만약 오디오 신호가 10 넘는 값이 들어오면, 이 임계값으로 제한된다 (아래에 설명)

 

The second type property is a variable stored property called maxInputLevelForAllChannels. This keeps track of the maximum input value that has been received by any AudioChannel instance. It starts with an initial value of 0.

두번째 maxInputLevelForAllChannels 라는 저장 타입 속성이다.

모든 AudioChannel 인스턴스들이 받을수 있는 최대 입력값을 계속 추적한다. 초기값은 0 이다.

 

The AudioChannel structure also defines a stored instance property called currentLevel, which represents the channel’s current audio level on a scale of 0 to 10.

AudioChannel 구조체는 저장 인스턴스 속성 currentLevel도 정의해서, 채널의 현재 오디오 레벨을 0 부터 10까지 비율로 표현한다.

 

The currentLevel property has a didSet property observer to check the value of currentLevel whenever it is set. This observer performs two checks:

currentLevel 속성은 didSet 옵저버를 가지고 있어서 이 값이 설정될때마다 값을 검사한다.

이 옵저버는 2가지 체크를 수행한다.

  • If the new value of currentLevel is greater than the allowed thresholdLevel, the property observer caps currentLevel to thresholdLevel.
  • If the new value of currentLevel (after any capping) is higher than any value previously received by any AudioChannel instance, the property observer stores the new currentLevel value in the maxInputLevelForAllChannels type property.
  • currentLevel의 새로운 값이 허용할수 있는 thresholdLevel 보다 크면, 옵저버가 currentLevel을 thresholdLevel 로 제한한다.
  • currentLevel의 새로운 값이 (제한된 후) 어떤 AudioChannel 인스턴스라도 이전에 받았던 값보다 크면,
    옵저버는 currentLevel 값을 maxInputLevelForAllChannels 타입 속성에 저장한다.

 

NOTE

In the first of these two checks, the didSet observer sets currentLevel to a different value. This does not, however, cause the observer to be called again.

이 2가지 검사중에서 첫번째 검사에서, didSet 옵저버는 currentLevel 을 다른 값으로 설정더라도,

옵저버가 다시 호출되지는 않는다.

 

You can use the AudioChannel structure to create two new audio channels called leftChannel and rightChannel, to represent the audio levels of a stereo sound system:

AudioChannel 구조체를 이용해서 leftChannel과 rightChannel이라는 2개의 새로운 오디오 채널을 만들수 있고,

스테레오 사운드 시스템의 오디오 레벨을 표현할수 있다.

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()

 

If you set the currentLevel of the left channel to 7, you can see that the maxInputLevelForAllChannels type property is updated to equal 7:

너가 왼쪽 채널의 currentLevel을 7로 설정하면, maxInputLevelForAllChannels 타입 속성이 똑같이 7로 업데이트 되는걸 볼수 있다.

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Prints "7"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "7"

 

If you try to set the currentLevel of the right channel to 11, you can see that the right channel’s currentLevel property is capped to the maximum value of 10, and the maxInputLevelForAllChannels type property is updated to equal 10:

너가 오른쪽 채널의 currentLevel을 11로 설정하려 하면, 오른쪽 채널의 currentLevel 속성이 최대값 10으로 제한되고,

maxInputLevelForAllChannels 타입 속성이 역시 10으로 업데이트 되는걸 볼수 있다.

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Prints "10"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "10"
반응형

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

[swift5.1번역] 12.Subscripts  (0) 2019.08.21
[swift5.1번역] 11.Methods  (0) 2019.08.19
[swift5.1번역] 9.Structures and Classes  (0) 2019.08.07
[swift5.1번역] 8.Enumeration  (0) 2019.08.01
[swift5.1번역] 7.Closures  (0) 2019.07.25
Posted by 돌비
,