[Swift 공식문서 읽기]Structures and Classes

llim🧚🏻‍♀️·2021년 8월 12일
1

Swift

목록 보기
9/26
post-thumbnail

안녕하세요. 엘림입니다🙇🏻‍♀️

Swift 공식 문서 정독하기 시리즈입니다!

제 스타일대로 정리했으니 추가적으로 더 필요한 정보는
공식문서 링크를 눌러 확인해주세용!

좀 더 편하게 보기위해 한국어로 번역된 사이트를 함께 확인했습니다!ㅎㅎ

자, 그럼 시작해볼까요

이 글은 공부하면서 작성한 글이기 때문에 잘못된 정보가 있을 수 있습니다.🥺
금방 잊어버릴... 미래의 저에게 다시 알려주기 위한 글이다보니
혹시라도 틀린 부분이 있다면, 댓글로 친절하게 알려주시길 부탁드립니다.🙏


클래스와 구조체

(클래스의 인스턴스는 전통적으로 객체 로 알려져 있습니다. 그러나, 스위프트의 구조체와 클래스는 다른 언어보다 훨씬 기능적으로 가깝습니다. 그리고 이 장의 대부분은의 클래스와 구조체 둘 모두의 인스턴스에 대한 기능적인 설명이기 때문에,일반적인 용어 인스턴스가 사용됩니다.)

클래스와 구조체의 비교

공통점

  • 값을 저장하기 위한 프로퍼티 정의
  • 기능을 제공하기 위한 메소드 정의
  • subscript 문법을 이용해 특정 값을 접근할 수 있는 subscript 정의
  • 초기 상태를 설정할 수 있는 initializer 정의
  • 기본 구현에서 기능 확장
  • 특정한 종류의 표준 기능을 제공하기 위한 프로토콜 순응

클래스만 가능한 기능

  • 상속 (Inheritance) : 클래스의 여러 속성을 다른 클래스에 물려 줌
  • 타입 캐스팅 (Type casting) : 런타임에 클래스 인스턴스의 타입을 확인
  • 소멸자 (Deinitializers) : 할당된 자원을 해제(free up) 시킴
  • 참조 카운트 (Reference counting) : 클래스 인스턴스에 하나 이상의 참조가 가능
    (구조체는 다른 코드로 전달될 때 항상 복사되서 전달되고, 참조 카운트(Reference counting)를 사용하지 않습니다.)

클래스만 지원하는 기능들은 복잡성(complexity)를 증가시킵니다. 일반적으로 구조체는 추론하기 쉽고(?), 클래스는 적절하거나 필요할때 사용하게 됩니다. 이 말은 실제로 정의하는 대부분의 사용자 정의 데이터 타입이 구조체와 열거형이 될 것을 의미합니다. 자세한 것은 클래스와 구조체 중에 선택하기에서 다시 다룹니다!

선언

class SomeClass {
    // 클래스 내용은 여기에
}
struct SomeStructure {
    // 구조체 내용은 여기에
}

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()  // 위 Resolution 구조체를 값으로 사용
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

새로운 클래스나 구조체를 선언할 때마다 Swift 에서 완전 새로운 타입을 선언하는 것입니다. 그래서 이름을 다른 표준 Swift 타입(String, Int, Bool)과 같이 UpperCamelCase 이름(SomeClass, SomeStructure 등)으로 선언합니다. 반대로 프로퍼티나 메소드는 lowerCamelCase(frameRate, incrementCount 등)으로 선언합니다.

구조체 초기화

구조체는 Initializer를 자동으로 생성해 제공합니다.

let vga = Resolution(width: 640, height: 480)

구조체와 열거형은 값 타입이다.

이것은 함수에서 상수나 변수에 전달될 때, 그 값이 복사되서 전달된다는 의미입니다.
사실 스위프트의 모든 기본 유형(integers, floating-point numbers, Booleans, strings, arrays and dictionaries...)은 값 유형이며 구조체로 구현되어있습니다.
모든 구조체와 열거형은 값 유형이며, 생성한 모든 구조체 및 열거형의 인스턴스와 속성으로 포함된 모든 값 유형은 항상 복사되어 전달됩니다.

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd


위 코드의 첫 줄에서 Resolution구조체의 인스턴스 hd를 선언합니다. 그리고 hd를 cinema라는 변수에 할당 했습니다. 그러면 이 cinema와 hd는 같을까요? 위에서 설명드린 것과 같이 할당(후 수정)하면 복사(copy)되기 때문에 cinema와 hd는 같지 않고 완전히 다른 인스턴스입니다.

enum CompassPoint {
    case north, south, east, west
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection = .east
if rememberedDirection == .west {
    print("The remembered direction is still .west")
}
// "The remembered direction is still .west" 출력

열거형에서도 마찬가지입니다.
현재 방향 currentDirection에 west를 할당하고 기억한 방양 rememberedDirection에 현재 방향 currentDirection을 저장합니다. 그리고 나서 현재 방향 currentDirection을 east로 변경합니다. 그리고 나서 기억하고 있는 인스턴스 rememberedDirection를 살펴보면 여전히 원본을 복사할 때의 값 west를 갖고 있는 것을 확인할 수 있습니다. 즉, 다른 인스턴스의 변화는 그 인스턴스에만 영향을 끼치고 그것과 가른 인스턴스에는 아무런 영향도 없다는 것을 알 수 있습니다.

클래스는 참조 타입이다.

값 타입과 달리 참조 타입은 변수나 상수에 값을 할당을 하거나 함수에 인자로 전달할 때 그 값이 복사되지 않고 참조 됩니다. 참조된다는 의미는 그 값을 갖고 있는 메모리를 바라보고 있다는 뜻 입니다.

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// "The frameRate property of tenEighty is now 30.0" 출력


가장 처음에 tenEighty.frameRate = 25.0 에서 할당한 25.0이 아니라 나중에 alsoTenEighty.frameRate = 30.0에서 변경한 30이 결과 값으로 출력되었습니다. 그 이유는 let alsoTenEighty = tenEighty코드에서 alsoTenEighty상수가 tenEighty인스턴스를 복사한 것이 아니라 참조한 것이기 때문입니다. 그래서 실제로 alsoTenEighty는 tenEighty가 바라보고 있는 메모리 주소를 동일하게 바라보고 참조하고 있는 것입니다.
여기서 한가지 더 의아하게 생각하실 수 있는 부분은 let alsoTenEighty = tenEighty로 alsoTenEighty를 상수로 선언했는데 어떻게 alsoTenEighty.frameRate = 30.0 같이 값을 변경할 수 있을까? 입니다. 이것은 alsoTenEighty자체를 변경하는 것이 아니라 그것이 바라보는 값을 변경하는 것이기 때문에 가능합니다.

식별 연산자

클래스는 참조 타입이기 때문에 여러 상수와 변수에서 같은 인스턴스를 참조할 수 있습니다. 상수와 변수가 같은 인스턴스를 참조하고 있는지 비교하기 위해 식별 연산자를 사용합니다.

  • === : 두 상수나 변수가 같은 인스턴스를 참조하고 있는 경우 참
  • !== : 두 상수나 변수가 다른 인스턴스를 참조하고 있는 경우 참
    (식별 연산자(===)는 비교 연산자(==)와 같지 않습니다. 식별 연산자는 참조를 비교하는 것이고, 비교 연산자는 값을 비교합니다.)
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// "tenEighty and alsoTenEighty refer to the same VideoMode instance." 출력

포인터

C, C++ 혹은 Objective-C를 사용해 보신 분이라면 이 참조라는 것이 포인터라고 생각하실 수 있습니다. Swift에서 상수나 변수가 특정 타입의 인스턴스를 참조하고 있다는 것은 위 포인터와 유사합니다. 하지만 표현에는 다른 점이 있는데요 C, C++, Objective-C에서 포인터는 실제 메모리를 직접 가리키고 있고 키워드로 표시하지만 Swift는 참조를 가르키기 위해 사용하지 않고 대신 다른 상수와 변수처럼 정의해 사용합니다.
(????)

클래스와 구조체 중에 선택하기

일반적으로 다음의 조건 중 1개 이상을 만족하면 구조체를 사용하는 것을 고려해 볼 수 있습니다.

  • 구조체의 주 목적이 관계된 간단한 값을 캡슐화(encapsulate) 하기 위한 것인 경우
  • 구조체의 인스턴스가 참조되기 보다 복사되기를 기대하는 경우
  • 구조체에 의해 저장된 어떠한 프로퍼티가 참조되기 보다 복사되기를 기대하는 경우
  • 구조체가 프로퍼티나 메소드 등을 상속할 필요가 없는 경우

String, Array, Dictionary의 할당과 복사 동작

Swift에서는 String, Array, Dictionary 같은 기본 데이터 타입이 구조체로 구현 돼 있습니다. 그렇다는 의미는 이 값을 다른 상수나 변수에 할당하거나 함수나 메소드에 인자로 넘길 때 이 값이 복사 된다는 것입니다. (하지만 위에서 말한 것 처럼, 실제 Swift에서는 최적의 성능을 위해 필요할 때만 데이터가 복사됩니다.)
반면 Foundation의 NSString, NSArray, NSDictionary는 클래스로 구현 돼 있습니다. 그래서 이 데이터들은 항상 할당 되거나 전달될 때 복사 되지 않고 참조가 사용됩니다. (표준 라이브러리는 포인터와 직접 상호 작용해야 하는 경우 사용할 수 있는 포인터 및 버퍼 유형을 제공합니다)


오늘도 스위프트 공식문서를 정리해보았군욥~
다음편도 힘내보겠습니다!

감사합니다🙇🏻‍♀️

profile
한달 차 iOS 개발자입니다🐥

0개의 댓글