2014년 6월 12일 목요일

Swift - 구조체(Structure) 훑어보기

C의 struct 라는 명령어는 구조체(Structure)를 정의하는 명령어다. 구조체는 여러 변수를 가지고 있는 타입의 일종이다. 스위프트도 이런 구조체 타입을 동일한 struct 명령을 이용해 만들 수 있다.

자세한 설명은 나중에 하고 우선 예제부터 보자.
struct MyDate {
    var BC = false
    var year = 1
    var month = 1
    var day = 1
}
MyDate라는 구조체를 정의했다. 생뚱맞게 기원전인지 아닌지를 체크하는 BC라는 변수와 년/월/일을 저장하기 위한 프로퍼티(멤버 변수)를 정의했다.

이렇게 정의한 MyDate는 아래와 같은 식으로 인스턴스화 해서 사용 할 수 있다.
var date1 = MyDate()

date1.year          // 1
date1.year = 2014
date1.year          // 2014
각 멤버 변수는 최초 초기화된 값으로 들어가 있고 이 값을 나중에 바꿀 수도 있다. var 로 변수가 선언되어 있어서 읽기와 쓰기가 모두 가능한데 만약 읽기 전용으로 쓰고 싶다면 let 으로 정의하면 된다.

여기까지는 C의 구조체와 상당히 비슷한 방식으로 정의하고 사용한다고 볼 수 있다. 그렇다면 이제 스위프트 구조체의 특징적인 모습을 보자.
struct MyDate {
    var BC = false
    var year = 0
    var month = 0
    var day = 0
    var name: String {
        return (self.BC ? "BC" : "AC") + " \(self.year)-\(self.month)-\(self.day)"
    }
}
MyDate의 일부를 수정했다. 이번에는 name이라는 문자열 프로퍼티(멤버변수)가 뭔가 이상한 내용을 가지고 있다.

아래는 사용 예제이다.
var date2 = MyDate()
date2.name          // "AC 0-0-0"
date2.BC = true
date2.year = 10
date2.month = 1
date2.day = 12
date2.name          // "BC 10-1-12"
name이라는 프로퍼티는 '어떤 값을 넘겨줄지를 코드로써 구현'한 것으로 이해할 수 있다. 참고로 이런 코드를 getter 라 부른다. 상세한 내용은 '프로퍼티'를 참고하자.

이제 조금 더 확장된 예제를 보자. 또 MyDate 를 뜯어고친다.
struct MyDate {
    var BC = false
    var year = 0
    var month = 0
    var day = 0
    var name: String {
        return (self.BC ? "BC" : "AC") + " \(self.year)-\(self.month)-\(self.day)"
    }
    
    init() { }
    
    init(y: Int, m: Int, d: Int, isBC: Bool = false) {
        self.BC = isBC
        self.year = y
        self.month = m
        self.day = d
    }
    
    func print() {
        println(self.name)
    }
}
init 이라는 함수랑 비슷하게 생긴 녀석이 두 가지 보이고 그 아래에는 함수 처럼 생긴 녀석도 등장한다. 그런데 실제로 함수일까?

Objective-C를 사용해 봤다면 init이 무엇인지는 알 것이다. 생성자 혹은 초기화 메소드이다. 여기서는 init을 두 가지 종류로 오버로딩 해 놓았다. 전자는 아무 인자 없이 생성할 때 사용하는 것인데 별 다른 코드 없이 기존에 정의된 초기값만 사용하는 용도이다. 후자는 인자로 멤버의 값을 할당하기 위해 사용한다.

마지막 print 라는건 func 라는 이름이 붙어서 함수라고 추측이 가능하다.

여기까지 보면 마치 클래스(class)와 비슷하다라고 느낀다면 정답이다. 사실 스위프트의 class와 struct는 큰 차이가 없다.
var date3 = MyDate()
date3.print()       // AC 0-0-0

var date4 = MyDate(y: 1234, m: 5, d: 6)
date4.print()       // AC 1234-5-6
date4.BC = true
date4.name          // "BC 1234-5-6"
date4.print()       // BC 1234-5-6
실제로 클래스의 인스턴스를 생성하는 것도 동일하다. 두 가지 생성 방법에 따라 init 함수(정확히는 메소드)도 두 가지 중 적당한 것으로 불러졌다. 그리고 print() 라는 메소드도 작동하는 것을 확인했다.

그럼 정리해보자.

struct

struct는 여러 변수를 담을 수 있는 컨테이너 타입을 정의하는 명령어다. 한글로 구조체(Structure)라 불리우는 c의 struct와 거의 동일한 용도로 사용된다.

하지만 스위프트의 struct는 멤버 변수(프로퍼티) 외에 함수(메소드)의 정의도 가능하다. 그래서 class와 비슷한 형식으로 사용 할 수 있다.

struct와 class가 다른 점은 몇 가지 있는데, 대표적인 차이로 class는 OOP의 중요한 특성인 상속이 가능하다는 특징이 있다. 반면 struct는 상속이 안된다. 대신 프로토콜이나 확장(extension) 등의 기능은 사용이 가능하다는 점에서 큰 차이가 없어보인다.

이 외에 struct는 class와 비교해서 몇 가지 기능이 빠져있다. 예를 들어 OOP의 파괴자(소멸자, 스위프트에선 Deinitializer로 부른다) 기능, 형변환과 관련된 기능, 그 밖에 레퍼런스 카운트 처리가 다르다는 등등... 물론 이 기능들은 비중이 그다지 크지는 않다.

스위프트에서 struct는 다양한 타입을 구성하는데 사용된다. 실제로 문자열 타입인 String을 보면 struct String 으로 선언이 되어있음을 알 수 있다. 상속이 필요하지만 않으면 class 대신 struct 로 코드를 만들어도 된다는 말이다.

struct를 사용함에 있어 알아둬야 할 한가지 참고사항이 더 있다면, struct의 메소드는 기본적으로 멤버 프로퍼티의 수정을 제한하고 있다. 클래스에 비하면 약간 어이없는 내용이지만, struct의 메소드에서 프로퍼티를 수정하고자 한다면 반드시 'mutating' 을 명시해야 한다. 자세한 사항은 메소드(Method) 글을 참고하자.

프로토콜(Protocol)

스위프트의 구조체는 프로토콜을 사용 할 수 있다.
struct SomeStruct: SomeProtocol {
    ...
}
모양이 마치 클래스에서 상속을 받는 것과 비슷한데, 앞서 이야기 했듯이 구조체는 상속을 지원하지 않는다. 이렇게 이름 오른쪽에 콜론(:) 이후 명시되는 이름은 무조건 프로토콜이라고 생각하자.

[관련글] Swift - 클래스(Class) 훑어보기
[관련글] Swift - 프로퍼티(Properties)
[관련글] Swift - 메소드(Method)

댓글 없음 :