본문으로 건너뛰기

Choosing Between Structures and Classes

해당 글의 내용은 아래 문서를 번역한 글입니다.

Decide how to store data and model behavior.


OverView

Structures and classes are good choices for storing data and modeling behavior in your apps, ut their similarities can make it difficult to choose one over the other.

데이터를 저장하거나 행동 모델링(modeling behavior)을 하는데, 구조체(structure)랑 클래스(class)는 좋은 선택이다. 그런데 둘이 비슷해서 어떤 것을 선택해야할지 어려울 수가 있다.

Consider the following recommendations to help choose which option makes sense when adding a new data type to your app.

다음과 같은 권장사항을 고려해서 앱에 새로운 데이터를 추가할 때 적합한 옵션을 선택하자.

  • Use structures by default. - Use classes when you need Objective-C interoperability. - Use classes when you need to control the identity of the data you’re modeling. - Use structures along with protocols to adopt behavior by sharing implementations.

  • 구조체를 기본으로 사용한다.
  • Objective-C와 상호운영이 필요할 경우 클래스를 사용한다.
  • 모델링 중 데이터의 ID제어가 필요할 경우 클래스를 사용한다.
  • 프로토콜과 함께 구조체를 사용해 구현을 공유하여 행동을 채택한다.

모델링 시 프로토콜을 활용하라는 의미로 생각된다.

Choose Structures by Default

Structure를 기본으로 사용하자

Use structures to represent common kinds of data. Structures in Swift include many features that are limited to classes in other languages: They can include stored properties, computed properties, and methods. Moreover, Swift structures can adopt protocols to gain behavior through default implementations. The Swift standard library and Foundation use structures for types you use frequently, such as numbers, strings, arrays, and dictionaries.

구조체를 사용해서 일반적인 데이터를 표현한다. 스위프트의 구조체는 다른 언어에서 클래스로 제한되는 많은 기능들을 포함하고 있다. 구조체는 stored properties, computed properties, methods를 갖고 있을 수 있다. 더 나아가 스위프트 구조체는 프로토콜을 채택해서 기본구현을 통해 행동을 얻을 수 있다. 스위프트 표준 라이브러리와 파운데이션은 number, string, array, dictionary 같이 자주 사용하는 타입을 위해 구조체를 사용한다.

Swift의 기본타입은 Structure로 정의되어있다.

Using structures makes it easier to reason about a portion of your code without needing to consider the whole state of your app. Because structures are value types—unlike classes—local changes to a structure aren’t visible to the rest of your app unless you intentionally communicate those changes as part of the flow of your app. As a result, you can look at a section of code and be more confident that changes to instances in that section will be made explicitly, rather than being made invisibly from a tangentially related function call.

구조체를 사용하면 앱의 전체 상태를 고려할 필요 없이 코드에 대해 더 쉽게 추론할 수 있다. 왜냐하면 구조체는 클래스와 달리 값 타입이므로 앱 흐름의 일부로 이러한 변경 사항을 전달하지 않는 한 구조체의 로컬 변경 사항은 앱의 나머지 부분에 표시되지 않는다. 결과적으로 코드 섹션을 보고 해당 섹션의 인스턴스에 대한 변경 사항이 연결된 함수에 의해 보이지 않게 변경이 이루어지지는 것이 아니라 명시적으로 수행될 것임을 확신할 수 있다.

Use Classes When You Need Objective-C Interoperability

Objective-C랑 같이 쓸일 있으면 클래스를 사용하자

If you use an Objective-C API that needs to process your data, or you need to fit your data model into an existing class hierarchy defined in an Objective-C framework, you might need to use classes and class inheritance to model your data. For example, many Objective-C frameworks expose classes that you are expected to subclass.

만약 데이터를 처리해야 하는 Objective-C API를 사용하거나 데이터 모델을 Objective-C 프레임워크에 정의된 기존 클래스 계층 구조에 맞춰야 하는 경우 클래스 및 클래스 상속을 사용하여 데이터를 모델링해야 할 수 있다. 예를 들어 많은 Objective-C 프레임워크는 서브클래스로 예상되는 클래스를 노출한다.

Use Classes When You Need to Control Identity

인스턴스의 식별이 필요하다면 클래스를 사용하자

Classes in Swift come with a built-in notion of identity because they’re reference types. This means that when two different class instances have the same value for each of their stored properties, they’re still considered to be different by the identity operator (===). It also means that when you share a class instance across your app, changes you make to that instance are visible to every part of your code that holds a reference to that instance. Use classes when you need your instances to have this kind of identity. Common use cases are file handles, network connections, and shared hardware intermediaries like

CBCentralManager

.

스위프트에서 클래스는 레퍼런스 타입이기 때문에 내장된 ID의 개념이 있다. 이는 두 개의 다른 클래스 인스턴스가 각각의 저장 프로퍼티에 대해 동일한 값을 가질 때 ID 연산자(===)를 통해 서로 다른 것으로 간주됨을 의미한다. 이는 또한 앱 전체에서 클래스 인스턴스를 공유할 때 해당 인스턴스에 대한 변경 사항이 해당 인스턴스에 대한 참조를 보유하는 코드의 모든 부분에 표시됨을 의미한다. 인스턴스가 이러한 종류의 식별이 필요 할 경우 클래스를 사용하라. 일반적인 사용 사례는 파일 처리(file handles), 네트워크 연결(network connections), CBCentralManager와 같은 공유 하드웨어 중계자(shared hardware intermediaries)가 있다.

For example, if you have a type that represents a local database connection, the code that manages access to that database needs full control over the state of the database as viewed from your app. It’s appropriate to use a class in this case, but be sure to limit which parts of your app get access to the shared database object.

예를 들어, 로컬 데이터베이스 연결을 나타내는 타입이 있는 경우, 해당 데이터베이스에 대한 접근을 관리하는 코드는 앱에서 볼 때 상태를 완전히 제어해야 한다. 이 경우 클래스를 사용하는 것이 적절하지만 공유 데이터베이스 객체에 접근할 수 있는 앱의 부분을 제한해야 한다.

데이터베이스를 관리하는 객체는 클래스로 작성하는 것이 좋지만, 이 객체를 아무데서나 호출하는 것은 적절하지 않다는 것 같다. 싱글톤과 같은 하나의 인스턴스로 생성 후, 필요한 경우만 호출하는 것이 좋을 것 같다.

Important

Treat identity with care. Sharing class instances pervasively throughout an app makes logic errors more likely. You might not anticipate the consequences of changing a heavily shared instance, so it’s more work to write such code correctly.

주의해서 ID를 다뤄라. 클래스 인스턴스를 앱 전체에서 광범위하게 공유하는 것은 논리적 오류의 발생 가능성이 높을 수 있다. 많이 공유되는 인스턴스를 변경하는 것은 예상치 못한 결과를 가져올 수 있으므로 이러한 코드를 올바르게 작성하는 것이 중요하다.

Use Structures When You Don’t Control Identity

인스턴스의 식별이 필요없다면 구조체를 사용하자

Use structures when you’re modeling data that contains information about an entity with an identity that you don’t control.

제어할 수 없는 ID를 가진 엔티티에 대한 정보가 포함한 데이터 모델링을 할 때 구조체를 사용한다.

In an app that consults a remote database, for example, an instance’s identity may be fully owned by an external entity and communicated by an identifier. If the consistency of an app’s models is stored on a server, you can model records as structures with identifiers. In the example below, jsonResponse contains an encoded PenPalRecord instance from a server:

예를 들어 원격 데이터베이스를 참조하는 앱에 인스턴스의 ID는 외부 엔티티가 완전히 소유하고 식별자를 통해 통신할 수 있다. 앱 모델의 일관성이 서버에 저장되어 있는 경우, ID가 있는 구조체로 레코드를 모델링할 수 있다. 아래의 예는, jsonResponse는 서버로부터 인코딩 된 PenPal 인스턴스가 포함되어 있다.

struct PenPalRecord {
let myID: Int
var myNickname: String
var recommendedPenPalID: Int
}

var myRecord = try JSONDecoder().decode(PenPalRecord.self, from: jsonResponse)

Local changes to model types like PenPalRecord are useful. For example, an app might recommend multiple different penpals in response to user feedback. Because the PenPalRecord structure doesn’t control the identity of the underlying database records, there’s no risk that the changes made to local PenPalRecord instances accidentally change values in the database.

PenPalRecord와 같은 모델 타입에 대한 로컬 변경은 유용하다. 예를 들어 앱은 사용자의 피드백에 대한 응답으로 여러 펜팔을 추천할 수 있다. PenPalRecord 구조체는 데이터베이스 레코드의 ID를 컨트롤할 수 없기 때문에 로컬 PenPalRecord 인스턴스가 변경되도 데이터베이스의 값이 실수로 변경될 위험이 없다.

If another part of the app changes myNickname and submits a change request back to the server, the most recently rejected penpal recommendation won’t be mistakenly picked up by the change. Because the myID property is declared as a constant, it can’t change locally. As a result, requests to the database won’t accidentally change the wrong record.

만약 앱의 다른 부분이 myNickName을 변경하고 변경 요청을 다시 서버에 제출하면 최근에 거부된 펜팔 추천이 실수로 선택되지 않는다. myID 프로퍼티는 상수로 선언되어 있기 때문에, 로컬에서 변경할 수 없기 때문이다. 결과적으로 데이터베이스의 대한 요청은 실수로 잘못된 레코드를 변경할 수 없다.

Use Structures and Protocols to Model Inheritance and Share Behavior

구조체랑 프로토콜을 사용하는 상속과 공유동작 모델링

Structures and classes both support a form of inheritance. Structures and protocols can only adopt protocols; they can’t inherit from classes. However, the kinds of inheritance hierarchies you can build with class inheritance can be also modeled using protocol inheritance and structures.

구조체와 클래스는 둘다 상속의 형태를 지원한다. 구조체와 프로토콜은 프로토콜만 채택할 수 있다. 구조체와 프로토콜은 클래스를 상속받을 수 없다. 그러나 클래스 상속으로 구축할 수 있는 상속 계층의 종류는 프로토콜 상속과 구조체를 사용하여 모델링할 수 있다.

If you’re building an inheritance relationship from scratch, prefer protocol inheritance. Protocols permit classes, structures, and enumerations to participate in inheritance, while class inheritance is only compatible with other classes. When you’re choosing how to model your data, try building the hierarchy of data types using protocol inheritance first, then adopt those protocols in your structures.

처음부터 상속 관계를 구축하는 경우 프로토콜 상속을 우선적으로 사용하는 것이 좋다. 프로토콜은 클래스, 구조체 및 열거형에 상속에 참여할 수 있도록 허용하지만 클래스 상속은 오직 클래스끼리만 호환된다. 데이터 모델링 방법을 선택할 때에 먼저 프로토콜 상속을 사용하여 데이터 유형의 계층 구조를 구축한 다음에 해당 프로토콜을 구조체에 채택한다.