Structures and Classes

Groot·2022년 6월 27일
0

Swift Language Guide

목록 보기
7/24
post-thumbnail
post-custom-banner

Swift Language Guide - Structures and Classes

  • 구조체와 클래스는 프로그램 코드의 빌딩 블록이 되는 유연한 범용 구조입니다.
  • 속성과 메서드를 정의하여 상수, 변수 및 함수를 정의하는 데 사용하는 것과 동일한 구문을 사용하여 구조체 및 클래스에 기능을 추가합니다.
  • Swift에서 단일 파일에 구조체 또는 클래스를 정의하면 해당 클래스 또는 구조체에 대한 외부 인터페이스가 자동으로 다른 코드에서 사용할 수 있게 됩니다.

    클래스의 인스턴스는 전통적으로 객체로 알려져 있습니다. 그러나 Swift 구조체와 클래스는 다른 언어보다 기능면에서 훨씬 더 가깝고 이 장의 대부분은 클래스 또는 구조체 유형의 인스턴스에 적용되는 기능에 대해 설명합니다. 이 때문에 보다 일반적인 용어 인스턴스가 사용됩니다.


📌 Comparing Structures and Classes

  • Swift의 구조체와 클래스에는 많은 공통점이 있습니다. 둘 다 다음을 수행할 수 있습니다.
    • 값을 저장할 속성 정의
    • 기능을 제공하는 메소드 정의
    • 첨자 구문을 사용하여 값에 대한 액세스를 제공하도록 첨자를 정의합니다.
    • 초기 상태를 설정하기 위한 이니셜라이저 정의
    • 기본 구현 이상으로 기능을 확장하도록 확장
    • 특정 종류의 표준 기능을 제공하는 프로토콜 준수
  • 클래스에는 구조체에 없는 추가 기능이 있습니다.
    • 상속을 통해 한 클래스가 다른 클래스의 특성을 상속할 수 있습니다.
    • 유형 캐스팅을 사용하면 런타임에 클래스 인스턴스의 유형을 확인하고 해석할 수 있습니다.
    • Deinitializers는 클래스의 인스턴스가 할당된 리소스를 해제할 수 있도록 합니다.
    • 참조 카운팅은 클래스 인스턴스에 대한 둘 이상의 참조를 허용합니다.
  • 클래스가 지원하는 추가 기능에는 복잡성이 증가합니다.
  • 일반적인 지침으로, 추론하기 쉽기 때문에 구조체를 선호하고 적절하거나 필요할 때 클래스를 사용합니다.
  • 실제로 이것은 정의하는 대부분의 사용자 정의 데이터 유형이 구조체 및 열거형이 될 것임을 의미합니다.
  • 더 자세한 비교는 구조체와 클래스 중 선택을 참조하십시오.

    클래스와 액터는 동일한 특성과 동작을 많이 공유합니다. 행위자에 대한 정보는 동시성을 참조하십시오.

📍 Definition Syntax

  • 구조체와 클래스는 유사한 정의 구문을 가지고 있습니다.
  • struct 키워드로 구조체를 도입하고 class 키워드로 클래스를 도입합니다.
  • 둘 다 한 쌍의 중괄호 안에 전체 정의를 배치합니다.
    struct SomeStructure {
        // structure definition goes here
    }
    class SomeClass {
        // class definition goes here
    }

    새 구조체나 클래스를 정의할 때마다 새 Swift 유형을 정의합니다. 표준 Swift 유형(예: String, Int 및 Bool)의 대문자 사용과 일치하도록 유형에 UpperCamelCase 이름(예: SomeStructure 및 SomeClass)을 지정합니다.속성 및 메서드에 lowerCamelCase 이름(예: frameRate 및 incrementCount)을 지정하여 유형 이름과 구별합니다.

  • 다음은 구조체 정의 및 클래스 정의의 예입니다.
    struct Resolution {
        var width = 0
        var height = 0
    }
    class VideoMode {
        var resolution = Resolution()
        var interlaced = false
        var frameRate = 0.0
        var name: String?
    }
  • 위의 예는 픽셀 기반 디스플레이 해상도를 설명하기 위해 Resolution이라는 새로운 구조체를 정의합니다.
  • 이 구조체에는 너비와 높이라는 두 개의 저장된 속성이 있습니다.
  • 저장된 속성은 묶어서 구조체나 클래스의 일부로 저장하는 상수 또는 변수입니다.
  • 이 두 속성은 초기 정수 값 0으로 설정하여 Int 유형으로 유추됩니다.
  • 위의 예는 또한 비디오 표시를 위한 특정 비디오 모드를 설명하기 위해 VideoMode라는 새 클래스를 정의합니다.
  • 이 클래스에는 4개의 가변 저장 속성이 있습니다.
  • 첫 번째인 resolution은 Resolution의 속성 유형을 유추하는 새로운 Resolution 구조체 인스턴스로 초기화됩니다.
  • 다른 세 속성의 경우 새 VideoMode 인스턴스는 false의 인터레이스 설정("비인터레이스 비디오"를 의미), 재생 프레임 속도 0.0 및 name이라는 선택적 문자열 값으로 초기화됩니다.
  • name 속성은 선택적 유형이기 때문에 기본값 nil 또는 "이름 값 없음"이 자동으로 지정됩니다.

📍 Structure and Class Instances

  • Resolution 구조체 정의와 VideoMode 클래스 정의는 Resolution 또는 VideoMode가 어떻게 생겼는지 설명합니다.
  • 그들 자체는 특정 해상도나 비디오 모드를 설명하지 않습니다.
  • 그렇게 하려면 구조체나 클래스의 인스턴스를 만들어야 합니다.
  • 인스턴스를 생성하는 구문은 구조체와 클래스 모두에서 매우 유사합니다.
    let someResolution = Resolution()
    let someVideoMode = VideoMode()
  • 구조체와 클래스는 모두 새 인스턴스에 대해 이니셜라이저 구문을 사용합니다.
  • 이니셜라이저 구문의 가장 간단한 형식은 Resolution() 또는 VideoMode()와 같이 빈 괄호 뒤에 오는 클래스 또는 구조체의 유형 이름을 사용합니다.
  • 그러면 속성이 기본값으로 초기화된 클래스 또는 구조체의 새 인스턴스가 생성됩니다.
  • 클래스 및 구조체 초기화는 초기화에서 자세히 설명합니다.

📍 Accessing Properties

  • 점 구문을 사용하여 인스턴스의 속성에 액세스할 수 있습니다.
  • 점 구문에서는 공백 없이 마침표(.)로 구분하여 인스턴스 이름 바로 뒤에 속성 이름을 씁니다.
    print("The width of someResolution is \(someResolution.width)")
    // Prints "The width of someResolution is 0"
  • 이 예에서 someResolution.width는 someResolution의 너비 속성을 참조하고 기본 초기 값인 0을 반환합니다.
  • VideoMode의 resolution 속성에 있는 width 속성과 같은 하위 속성으로 드릴다운할 수 있습니다.
    print("The width of someVideoMode is \(someVideoMode.resolution.width)")
    // Prints "The width of someVideoMode is 0"
  • 점 구문을 사용하여 변수 속성에 새 값을 할당할 수도 있습니다.
    someVideoMode.resolution.width = 1280
    print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
    // Prints "The width of someVideoMode is now 1280"

📍 Memberwise Initializers for Structure Types

  • 모든 구조체에는 자동으로 생성된 멤버별 이니셜라이저가 있어 새 구조체 인스턴스의 멤버 속성을 초기화하는 데 사용할 수 있습니다.
  • 새 인스턴스의 속성에 대한 초기 값은 이름으로 멤버별 이니셜라이저에 전달할 수 있습니다.
    let vga = Resolution(width: 640, height: 480)
  • 구조체와 달리 클래스 인스턴스는 기본 멤버별 이니셜라이저를 받지 않습니다.
  • 이니셜라이저는 초기화에 자세히 설명되어 있습니다.

📌 Structures and Enumerations Are Value Types

  • 값 유형은 변수나 상수에 할당되거나 함수에 전달될 때 값이 복사되는 유형입니다.

  • 사실, Swift의 모든 기본 유형(정수, 부동 소수점 숫자, 부울, 문자열, 배열 및 사전)은 값 유형이며 배후에서 구조체로 구현됩니다.

  • 모든 구조체와 열거형은 Swift의 값 유형입니다. 즉, 생성한 모든 구조체 및 열거형 인스턴스와 속성으로 포함된 모든 값 유형은 코드에서 전달될 때 항상 복사됩니다.

    배열, 사전 및 문자열과 같은 표준 라이브러리에 의해 정의된 컬렉션은 최적화를 사용하여 복사의 성능 비용을 줄입니다. 즉시 복사본을 만드는 대신 이러한 컬렉션은 원본 인스턴스와 복사본 간에 요소가 저장되는 메모리를 공유합니다. 컬렉션 복사본 중 하나가 수정되면 수정 직전에 요소가 복사됩니다. 코드에서 볼 수 있는 동작은 항상 복사가 즉시 발생한 것처럼 보입니다.

  • 이전 예의 Resolution 구조체를 사용하는 다음 예를 고려하십시오.

    let hd = Resolution(width: 1920, height: 1080)
    var cinema = hd
  • 이 예제는 hd라는 상수를 선언하고 풀 HD 비디오(폭 1920픽셀 x 높이 1080픽셀)의 너비와 높이로 초기화된 Resolution 인스턴스로 설정합니다.

  • 그런 다음 시네마라는 변수를 선언하고 HD의 현재 값으로 설정합니다.

  • Resolution은 구조체이기 때문에 기존 인스턴스의 복사본이 만들어지고 이 새 복사본이 영화관에 할당됩니다.

  • 이제 HD와 시네마의 너비와 높이가 동일하지만 장면 뒤에서 완전히 다른 두 인스턴스입니다.

  • 다음으로, 시네마의 너비 속성은 디지털 시네마 프로젝션에 사용되는 약간 더 넓은 2K 표준의 너비로 수정됩니다(2048픽셀 너비 및 1080픽셀 높이)

    cinema.width = 2048
  • Cinema의 width 속성을 확인하면 실제로 2048로 변경되었음을 알 수 있습니다.

    print("cinema is now \(cinema.width) pixels wide")
    // Prints "cinema is now 2048 pixels wide"
  • 그러나 원래 hd 인스턴스의 너비 속성에는 여전히 1920의 이전 값이 있습니다.

    print("hd is still \(hd.width) pixels wide")
    // Prints "hd is still 1920 pixels wide"
  • 시네마에 현재 HD 값이 주어지면 hd에 저장된 값이 새 시네마 인스턴스에 복사됩니다.

  • 최종 결과는 동일한 숫자 값을 포함하는 완전히 별개의 두 인스턴스였습니다.

  • 그러나 별개의 인스턴스이기 때문에 아래 그림과 같이 시네마 너비를 2048로 설정해도 hd에 저장된 너비에는 영향을 미치지 않습니다.

  • 열거형에도 동일한 동작이 적용됩니다.

    enum CompassPoint {
        case north, south, east, west
        mutating func turnNorth() {
            self = .north
        }
    }
    var currentDirection = CompassPoint.west
    let rememberedDirection = currentDirection
    currentDirection.turnNorth()
    
    print("The current direction is \(currentDirection)")
    print("The remembered direction is \(rememberedDirection)")
    // Prints "The current direction is north"
    // Prints "The remembered direction is west"
  • storedDirection에 currentDirection 값이 할당되면 실제로 해당 값의 복사본으로 설정됩니다.

  • 그 이후에 currentDirection의 값을 변경해도 RememberDirection에 저장된 원래 값의 복사본에는 영향을 미치지 않습니다.

📌 Classes Are Reference Types

  • 값 유형과 달리 참조 유형은 변수나 상수에 할당되거나 함수에 전달될 때 복사되지 않습니다.
  • 복사본 대신 동일한 기존 인스턴스에 대한 참조가 사용됩니다.
  • 다음은 위에서 정의한 VideoMode 클래스를 사용하는 예입니다.
    let tenEighty = VideoMode()
    tenEighty.resolution = hd
    tenEighty.interlaced = true
    tenEighty.name = "1080i"
    tenEighty.frameRate = 25.0
  • 이 예제에서는 tenEighty라는 새 상수를 선언하고 VideoMode 클래스의 새 인스턴스를 참조하도록 설정합니다.
  • 비디오 모드에는 이전의 1920 x 1080 HD 해상도 사본이 할당됩니다.
  • 인터레이스로 설정되고 이름은 "1080i"로 설정되며 프레임 속도는 초당 25.0프레임으로 설정됩니다.
  • 다음으로, tenEighty는 alsoTenEighty라는 새로운 상수에 할당되고 alsoTenEighty의 프레임 속도는 수정됩니다
    let alsoTenEighty = tenEighty
    alsoTenEighty.frameRate = 30.0
  • 클래스는 참조 유형이므로 tenEighty 및 alsoTenEighty는 실제로 동일한 VideoMode 인스턴스를 참조합니다. 사실상, 아래 그림과 같이 동일한 단일 인스턴스에 대한 두 개의 다른 이름일 뿐입니다.
  • tenEighty의 frameRate 속성을 확인하면 기본 VideoMode 인스턴스에서 새 프레임 속도 30.0을 올바르게 보고한다는 것을 알 수 있습니다.
    print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
    // Prints "The frameRate property of tenEighty is now 30.0"
  • 이 예제는 또한 참조 유형이 어떻게 추론하기 더 어려울 수 있는지 보여줍니다.
  • 프로그램 코드에서 tenEighty와 alsoTenEighty가 멀리 떨어져 있으면 비디오 모드가 변경되는 모든 방법을 찾기 어려울 수 있습니다.
  • tenEighty를 사용할 때마다 alsoTenEighty를 사용하는 코드에 대해서도 생각해야 하며 그 반대의 경우도 마찬가지입니다.
  • 대조적으로, 값 유형은 동일한 값과 상호 작용하는 모든 코드가 소스 파일에서 함께 가깝기 때문에 추론하기가 더 쉽습니다.
  • 그러나 tenEighty 및 alsoTenEighty 상수 자체의 값은 실제로 변경되지 않기 때문에 여전히 tenEighty.frameRate 및 alsoTenEighty.frameRate를 변경할 수 있습니다.
  • 변경된 것은 해당 VideoMode에 대한 상수 참조 값이 아니라 기본 VideoMode의 frameRate 속성입니다.

📍 Identity Operators

  • 클래스는 참조 유형이기 때문에 여러 상수와 변수가 뒤에서 클래스의 동일한 단일 인스턴스를 참조할 수 있습니다.
  • (구조체 및 열거형의 경우에는 동일하지 않습니다. 상수 또는 변수에 할당되거나 함수에 전달될 때 항상 복사되기 때문입니다.)
  • 두 개의 상수 또는 변수가 클래스의 정확히 동일한 인스턴스를 참조하는지 여부를 찾는 것이 때때로 유용할 수 있습니다. 이를 가능하게 하기 위해 Swift는 두 개의 ID 연산자를 제공합니다.
    • Identical to (===)
    • Not identical to (!==)
  • 다음 연산자를 사용하여 두 개의 상수 또는 변수가 동일한 단일 인스턴스를 참조하는지 확인합니다.
    if tenEighty === alsoTenEighty {
        print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
    }
    // Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
  • same to(3개의 등호 또는 ===로 표시됨)는 equal to(2개의 등호 또는 ==로 표시됨)와 동일한 것을 의미하지 않습니다.
  • 동일하다는 것은 클래스 유형의 두 상수 또는 변수가 정확히 동일한 클래스 인스턴스를 참조한다는 것을 의미합니다.
  • 같음은 유형 디자이너가 정의한 같음의 적절한 의미에 대해 두 인스턴스가 값이 같거나 동등한 것으로 간주됨을 의미합니다.
  • 고유한 사용자 정의 구조체와 클래스를 정의할 때 두 인스턴스가 동일한 것으로 자격이 되는 항목을 결정하는 것은 귀하의 책임입니다.
  • == 및 != 연산자의 고유한 구현을 정의하는 프로세스는 등가 연산자에 설명되어 있습니다.
  • 고유한 사용자 정의 구조체와 클래스를 정의할 때 두 인스턴스가 동일한 것으로 자격이 되는 항목을 결정하는 것은 귀하의 책임입니다.

📍 Pointers

  • C, C++ 또는 Objective-C에 대한 경험이 있는 경우 이러한 언어가 포인터를 사용하여 메모리의 주소를 참조한다는 것을 알 수 있습니다.
  • 어떤 참조 유형의 인스턴스를 참조하는 Swift 상수 또는 변수는 C의 포인터와 유사합니다.
  • 그러나 메모리의 주소에 대한 직접적인 포인터가 아니며 참조를 생성하고 있음을 나타내기 위해 별표(*)를 작성할 필요가 없습니다.
  • 대신 이러한 참조는 Swift의 다른 상수 또는 변수처럼 정의됩니다.
  • 표준 라이브러리는 포인터와 직접 상호 작용해야 하는 경우 사용할 수 있는 포인터 및 버퍼 유형을 제공합니다(수동 메모리 관리 참조).
profile
I Am Groot
post-custom-banner

0개의 댓글