클래스와 구조체
- 클래스와 구조체는 소스 코드에서 코드 블록을 구성하고 있다. 프로퍼티와 메소드 정의를 통해 기능을 추가한다. 상수, 변수, 함수와 동일한 문법을 사용한다.
- 다른 프로그래밍 언어와 달리 swift는 별도의 인터페이스나 구현 파일을 생성할 필요가 없다. 하나의 파일에 클래스나 구조체를 정의할 수 있어서 외부 인터페이스는 자동으로 다른 코드가 사용하는 것을 가능하게 한다.
- 주의 : 클래스의 인스턴스를 보통 객체라고 한다. 그러나 Swift에서 클래스와 구조체는 다른 언어에서보다 기능에 훨씬 가깝다. 이 챕터의 많은 분량이 클래스나 구조체 타입의 인스턴스에 적용가능한 기능을 설명하는데 할애한다. 이런 이유로 좀더 일반적인 용어인 인스턴스를 사용한다.
클래스 vs. 구조체
- 공통점
- 값을 저장하는 프로퍼티 정의
- 기능을 제공하는 메소드 정의
- 값에 접근을 위한 subscript 정의
- 초기 상태를 설정하기 위한 초기화 정의
- 기본 구현에 추가 기능을 통한 확장
- 표준 기능을 제공하는 프로토콜 충족시키기
- Class의 추가 기능
- 상속 : 다른 클래스의 속성을 상속
- 타입 캐스팅 : 런타임에서 클래스 인스턴스의 타입을 검사하고 해석
- 할당한 리소스를 해제하는 deinitializer
- 참조 카운팅 : 클래스 인스턴스에 대해서 1개 이상 참조를 허용한다.
- 구조체는 항상 코드에서 전달할 때 복사되어 전달된다. 따라서 참조 카운팅을 사용하지 않는다.
문법
클래스와 구조체는 비슷한 문법을 사용한다. 앞에
class와struct를 각각 사용한다.class SomeClass { } struct SomeStructure{ }- 주의: 새로운 클래스나 구조체를 정의할 때마다 새로운 swift 타입을 정의하는 것과 같다. 클래스나 구조체를 정의할 때는 대문자를 사용한다. SomeClass나 SomeStructure와 같이. 대신에 메소드나 프로퍼티를 정의하는 경우 소문자를 사용한다.
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interfaced = false
var frameRate = 0.0
var name: String?
}
- 위 예제에서
Resolution이라는 구조체를 만들었다. 이 구조체는 너비, 높이를 값을 저장하고 있다. 값을 저장하는 프로퍼티는 상수나 변수가 가능하다. Int 타입이며 초기값으로 0을 할당하였다. - VideMode라는 클래스를 정의했다. 이 클래스는 비디오 디스플레이에서 특정 비디오 모드를 지정한다. 값을 저장하는 4개 프로퍼티를 가지고 있다. 첫번째는 Resolution 구조체 인스턴스를 interlaced는 false를 frameRate는 0.0을 name은 자동으로 nil을 기본값으로 한다.
- name의 경우
?를 사용했는데 이는 선택적 타입(optional type)이라고 부른다.
클래스와 구조체 인스턴스
위에서 Resolution 구조체와 VideoMode 클래스는 정의를 알아봤다. 특정 Resolution이나 VideoMode를 가지기 위해서는 인스턴스를 생성해야 한다.
let someResolution = Resolution() let someVideoMode = VideoMode()구조체와 클래스 모두 새로운 인스턴스에 대해서 초기화 문법을 사용한다. 초기화 문법의 가장 단순한 형태는 클래스나 구조체의 타입 이름을 그대로 사용하는 것이다. 여기서는 Resolution()나 VideoMode()와 같은 형태가 된다. 이렇게 하면 새로운 인스턴스를 생성할 때 기본 값으로 프로퍼티 값이 기본값으로 설정되게 된다.
프로퍼티 접근
- 인스턴스의 프로퍼티에 접근하는 방법을 알아보자. 인스턴스 이름 뒤에 바로
.을 붙이고 프로퍼티 이름을 연결해서 쓴다.print("The width of someResolution is \(someResolution.width)") - SomeResolution의 width 프로퍼티는 someResolution.width와 같이 표현한다.
- 다음으로는 VideoMode의 프로퍼티인 resolution의 프로퍼티인 wdith을 살펴보자. 여기서도 동일하게
.으로 연결되는 것을 볼 수 있다.
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
구조체 타입에서 멤버 초기화
- 모든 구조체는 자동으로 생성된 멤버 초기화가 있다. 이는 구조체 인스턴스의 멤버 프로퍼티를 초기화하는데 사용한다. 이 프로퍼티에 초기값을 지정하는 경우 이름을 함께 사용한다.
let vga = Resolution(width: 640, height: 480)
- 구조체와 달리 클래스 인스턴스는 기본 멤버 초기화를 사용하지 않는다.
구조체와 열거형의 값 타입이다.
- 값 타입(value type)이란 변수나 상수에 할당되거나 함수에서 인자로 전달되는 경우 값이 복사된다는 것을 뜻한다.
- 값 타입을 사용하는 경우 비요잉 . 모든 기본 타입은 값 타입으로 구조체로 구현되어 있다.
- 모든 구조체와 열거형은 swift에서 값 타입이다. 이 말은 여러분이 생성한 구조체와 열거형 인스턴스는 항상 전달될때 복사된다는 것이다. 여기에는 프로퍼티로 가지고 있는 값 타입도 포함된다.
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
- 이 예제에서는 hd라는 상수를 정의하고 여기에 Resolution 인스턴스를 생성하였다. 다음으로 cinema라는 변수를 선언하고 여기에 hd 값으로 설정하였다. Resolution는 구조체이기 때문에, 기존 인스턴스으 복사가 일어나고 이 새로운 복사본이 cinema에 할당된다. hd와 cinema가 이제 동일한 width와 height를 갖게 되지만 이 인스턴스들은 서로 다른 인스턴스이다.
- cinema의 width 프로퍼티는 2K에 맞게 수정해보자.(2048 width와 1080 height)
cinema.width = 2048
이제 cinema의 width 프로퍼티를 검사해서 2048로 변경되었는지 확인해 보자.
print("cinema is now \(cinema.width) pixels wide")하지만 hd의 width 프로퍼티는 여전히 1920이다.
print("hd is still \(hd.width) piexels wide")hd 값을 cinema에 할당할 때, hd에 저장된 값을 새 cinema 인스턴스에 복사한다. 따라서 이 두 값은 완전히 독립된 인스턴스이다. 따라서 cinema에 2048 값을 설정해도 hd에 저장된 width에는 영향이 없다.
enum에서도 동일한 동작이 가능하다.
enum CompassPoint {
case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberdDirection == .West {
print("The remembered direction is still .west")
}
- rememberdDirection에 currentDirection을 할당한다. 이렇게 하면 값의 복사값으로 설정된다. 따라서 currentDirection의 값이 바뀌어도 remberdDirection에 저장된 값은 영향을 받지 않는다.
클래스는 참조 타입이다.
- 값 타입과 달리 참조 타입은 변수나 상수 그리고 함수에 인자로 전달하는 경우 복사본을 전달하지 않는다. 대신 기존 인스턴스에 대한 참조를 전달한다.
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
tenEighty라는 상수를 선언하고 여기에 VideoMode의 새로운 인스턴스에 대한 참조로 설정한다. 각 프로퍼티에 값을 할당했다.- tenEighty를 새로운 상수 alsoTenEighty에 할당하고 alsoTenEighty의 프레임 레이트를 수정한다.
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
- 클래스는 참조 타입이므로 tenEighty와 alsoTenEighty는 실제로 동일한 VideoMode 인스턴스를 참조하고 있다. 동일한 인스턴스에 다른 이름을 가지고 있다고 말할 수 있다.
- tenEighty의 frameRate 프로퍼티를 확인해 보면 30.0으로 설정된 것을 확인할 수 있다.
print("The frameRate proeprty of tenEighty is now\(tenEighty.frameRate)")
- tenEighty와 alsoTenEighty는 변수가 아니라 상수로 선언하였다. 그러나 tenEighty.frameRate와 alsoTenEighty.frameRate는 변경할 수 잇다. tenEighty와 alsoTenEighty 상수의 값 자체는 변경되지 않는다. 이 말은 이 상수 자체에서 인스턴스의 값을 저장하고 있지 않다는 뜻이다. 참조하는 인스턴스의 값이 바뀐 것이지 상수의 값이 변경된 것이 아니다.
확인 연산자
- 클래스는 참조 타입이기 때문에, 동일한 인스턴스를 참조하는 여러 상수나 변수가 가능하다. 따라서 2개 상수나 변수가 동일한 인스턴스를 가리키고 있는지를 알기 위한 방법이 필요하다.
- 동일한 경우 : '==='
- 동일하지 않는 경우 '!=='
- 예제를 통해 보자
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
- 영어로
동일(identical to) ===하냐같은 값(equal to) ==이냐의 차이점은- 동일 : 클래스 타입의 2개 상수나 변수가 정확히 동일한 클래스 인스턴스를 가리키는 경우
- 같은 값 : 2개 인스턴스가 가지고 있는 값이 같다는 뜻이다. 같다라는 것은 해당 타입을 설계한 사람이 정의할 수 있다.
- 여러분이 직접 클래스나 구조체를 정의할 때, 2개 인스턴스가 동일하다는 정의를 직접할 수 있다.
포인터
- C/C++과 같은 언어에 경험이 있다면 포인터라 함은 메모리 주소를 가리킨다는 것을 알 것이다. swift 상수나 변수는 참조 타입의 인스턴스를 참조하므로 C에서의 포인터와 유사하다. 하지만 메모리 상에 주소를 직접 가리키는 것은 아니며 문법적으로는 (*)를 사용하지 않는다. 대신에 swift에서 참조는 다른 상수나 변수처럼 정의할 수 있다.
클래스와 구조체 사이에서 선택하기
- 클래스나 구조체를 이용하면 여러분이 원하는 데이터 타입을 정의할 수 있다.
- 구조체 인스턴스는 항상 값으로 전달되고 클래스 인스턴스는 항상 참조로 전달된다. 이말은 각각 용도가 다르다는 말이다. 여러분이 프로젝트에 필요한 자료구조나 기능을 고려할 때, 각 데이터 구조를 클래스로 할 것인지 아니면 구조체로 할 것인지를 정해야 한다는 말이다.
- 가이드 라인
- 구조체의 주요 목적은 몇개 관련있는 간단한 데이터 값을 모으는 역할이다.
- 또 이 인스터스는 값으로 전달하는 경우에 적합하다.
- 구조체에 저장된 프로퍼티 자체는 값 타입이고 이는 참조가 아니라 복사하는 경우에 적합하다.
- 구조체는 기존 타입의 프로퍼티나 메소드를 상속할 필요가 없는 경우에 적합하다.
- 구조체에 적합한 예제 가이드
- 기하학 모양 : width, hegiht
- 범위를 나타낼 때 : start, length
- 3D 좌표 : x, y, z
- 이외에 경우에는 클래스를 정의하고 참조로 전달하는 클래스의 인스턴스를 생성한다. 실제로 이 말은 대부분 개발자가 작성하는 데이터 구조는 구조체가 아니라 클래스다.
String, Array 그리고 Dictionary에서 할당과 복사 동작
- String, Array, Dictionary는 구조체와 같이 동작한다.
- 상수나 변수에 할당하거나 함수에 인자로 전달하는 경우 복사가 일어난다.
- NSString, NSArray, NSDictionary는 클래스이므로 클래스처럼 참조로 동작한다.
- 주의 : 위에서 string, array, dictionary에서 복사가 일어난다고 했지만 실제로 밑단에서는 꼭 필요한 경우에만 복사를 한다. 성능을 위해서 swift가 알아서 관리한다.
정의
사용용도
문법
예제
Quiz
- 정의에 대한 질문
- 용도에 대한 질문
- 예제 관련