---
layout: post
title: [스위프트 대충보기] 8. 클래스(class)와 구조체(structure)
tags: 스위프트, swift, 클래스, 구조체, class, struct
---
- 클래스/구조체: 프로그램을 구축할 때 범용으로 유연하게 사용할 수 있는 프로그램 구성 요소
-
안에 값을 저정할 프로퍼티(property)를 정의할 수 있다
-
안에 메서드를 정의할 수 있다
-
[]를 사용해 첨자(subscript) 문법으로 내부의 값을 액세스할 수 있는 첨자를 정의할 수 있다
-
초기 상태 설정을 위한 초기화 함수(initializer)를 정의할 수 있다
-
클래스/구조체 구현을 확장할 수 있다
-
어떤 조건의 기능을 제공하기 위한 프로토콜을 (conform) 만족시킬(conform) 수 있다
-
클래스만 더 가지는 특징
-
상속을 통해 다른 클래스의 특성을 이어받을 수 있다
-
타입 캐스팅을 통해 실행 시점에 클래스 인스턴스의 타입을 해석하고 검사할 수 있다
-
해제함수(deinitializer)를 사용해 사용한 자원을 반환할 수 있다
-
참조 카운팅을 통해 한 클래스 인스턴스를 여러 군데에서 참조(사용)할 수 있다
-
-
클래스 정의하기: class 이름 { ... }
-
구조체 정의하기: struct 이름 { ... }
-
클래스/구조체를 정의하면 새로운 타입을 정의하는 것이다
-
관습적으로 타입 이름에는 대문자 낙타등표기법(UpperCamelCase)를 사용한다. 클래스/구조체 안의 프로퍼티나 메서드는 소문자 낙타등표기법(lowerCamelCase)를 사용한다.
struct Resolution { var width = 0 var height = 0 } class VideoMode { var resolution = Resolution() var interlaced = false var frameRate = 0.0 var name: String? }
-
인스턴스 생성: 타입이름() 형태로 생성한다
let someResolution = Resolution() let someVideoMode = VideoMode()
-
프로퍼티 액세스: 인스턴스이름.프로퍼티이름
println("resolution=\(someResolution.width)") someVideoMode.resolution.width = 1280 println("The width of someVideoMode is now \(someVideoMode.resolution.width)")
-
구조체의 경우 디폴트 초기화 함수로 멤버별 초기화(memberwise initializer)를 자동 제공한다. 클래스의 경우 그런거 없다.
let vga = Resolution(width: 640, height: 480)
-
구조체나 열겨형은 값 타입(value type)이다. 값 타입은 호출, 대입 등이 일어날 때 복사를 한다는 뜻이다.
let hd = Resolution(width: 1920, height: 1080) var cinema = hd
- 위 예에서, cinema는 hd와 같은 인스턴스를 가리키는 것이 아니고, 내용을 복사한 새 인스턴스를 만들어 가리킨다. 따라서 cinema의 프로퍼티를 변경해도 hd에는 영향을 끼치지 못한다.
-
클래스는 참조 타입(reference type)이다. 따라서, 대입시 참조(주소라고 생각하면 됨)가 복사된다.
let tenEighty = VideoMode() tenEighty.resolution = hd tenEighty.interlaced = true tenEighty.name = "1080i" tenEighty.frameRate = 25.0 // alsoTenEighty는 tenEighty랑 같은 인스턴스를 가리킨다 let alsoTenEighty = tenEighty alsoTenEighty.frameRate = 30.0 // 따라서 tenEighty의 프로퍼티도 값이 30.0으로 바뀐다 println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
-
식별(identity) 연산: 두 변수가 같은 인스턴스를 가리키는지 비교하는 연산
- ===, !===
-
두 변수/상수가 동일한 것(identity)과 같은 것(equality)은 다르다. 동일한 것은 대상 객체(참조 대상 또는 번지)가 같다는 뜻이고, 같은 것은 의미적으로 둘이 같다는 뜻이다.
-
언제 클래스를 쓰고 언제 구조체를 쓰나?
-
기본적으로, 클래스는 참조타입, 구조체는 값 타입이란 것에 착안할 것
-
구조체는 간단한 데이터 값들을 한데 묶어서 사용하는 경우를 주로 다룬다.
-
전체 덩어리 크기가 작은 경우, 복사를 통해 전달해도 좋은 경우 구조체를 쓴다.
-
-
구조체는 기존 타입의 특성을 상속할 필요가 없다.
-
-
구조체를 쓰는게 좋은 경우(예제): "The C++ 프로그래밍 언어"의 10장 3절(3판기준)에서 이야기하듯, 어떤 프로그래밍 언어의 원시 타입(primitive type)과 다름없이 사용 가능한 작은 구체적 타입(small concrete type)을 목적으로 할 때 사용하면 좋음(물론 상속할 필요가 없는 경우에 구조체를 사용)
-
너비, 높이를 표현하는 기하학적 모양을 처리할 경우
-
시작값, 증분, 길이 등으로 순열을 표현할 경우
-
3차원 좌표 시스템의 각 좌표
-
-
Array와 Dictionary는 구조체임
-
하지만 배열은 딕셔너리나 다른 구조체와 다름
-
또한, Array와 NSArray, NSDictionary도 다름. NSArray, NSDictionary는 참조 타입임
-
의미상으로는 복사연산이지만 스위프트는 내부적으로는 복사가 꼭 필요할 때만 복사를 수행함(사족: copy on write등을 사용?)
-
딕셔너리 복사:
-
딕셔너리를 변수나 상수에 대입하거나 인자로 함수/메서드에 넘기면 그 딕셔너리를 복사함. 키/값들도 복사함. 물론 키나 값이 참조타입이면 참조만 복사함.
var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] var copiedAges = ages copiedAges["Peter"] = 24 println(ages["Peter"]) // "23" 출력
-
-
배열 복사: 특이함!
-
기존 배열을 다른 변수나 상수에 대입하면 참조를 복사하는 셈임. 한쪽의 변경사항(원소 변경)을 다른 참조에서도 관찰 가능
-
배열의 길이를 바꾸는 연산을 수행할 때 배열을 두 벌로 분리함. 즉, 원소를 삭제하거나, 일부분을 치환하거나 하면 그때 복사가 일어남. 이때는 딕셔너리와 마찬가지로 원소가 값 타입이면 원소를 전체 복사, 원소가 참조 타입이면 참조를 복사함
var a = [1, 2, 3] var b = a var c = a println(a[0]) // 1 println(b[0]) // 1 println(c[0]) // 1 a[0] = 42 println(a[0]) // 42 println(b[0]) // 42 println(c[0]) // 42 a.append(4) a[0] = 777 println(a[0]) // 777 println(b[0]) // 42 println(c[0]) // 42
-
-
별도의 배열 복사본을 확보한 다음 무언가를 실행하고 싶은 경우가 있을 것임. 그럴때는 unshare()를 사용
// 위의 배열 예제에서 이어서... b.unshare() b[0] = -105 println(a[0]) // 777 println(b[0]) // -105 println(c[0]) // 42
-
배열 비교시 ===를 사용하면 두 배열이 메모리상에서 동일한 인스턴스인지를 비교
-
원소가 같은지 알고 싶다면, 첨자에 ...나 ..를 써서 부분배열을 비교 하면 됨
if b[0...1] === b[0...1] { println("These two subarrays share the same elements.") } else { println("These two subarrays do not share the same elements.") }
-
강제복사: copy() 메서드 사용. 얕은 복사(shallow copy)를 수행함.
var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"] var copiedNames = names.copy() copiedNames[0] = "Mo" println(names[0]) // names와 copiedNames는 무관하기 때문에, Mohsen을 출력
-
copy()는 무조건 복사를 수행하고, unshare()는 꼭 필요할 때까지 최대한 복사를 미룬다.