Swift - Classes and Structures ( 클래스와 구조체 )
클래스와 구조체는 프로그램 코드를 구조화 시키기 위한 일반적이고 유연한 방법이다.
속성과 메서드를 정의하여 상수, 변수 및 함수를 정의하는 데 사용하는 것과 동일한 구문을 사용하여 구조체와 클래스에 기능을 추가할 수 있다.
Class vs Structure
공통점
- 값을 저장하기 위한 프로퍼티를 정의할 수 있다.
- 기능을 제공하기 위한 메소드를 정의할 수 있다.
- subscript 문법을 활용해 특정 값을 접근할 수 있도록 subscript를 정의할 수 있다.
- 이니셜라이즈를 위한 init 함수를 정의할 수 있다.
- 기본 구현에서 기능을 확장시킬 수 있다.
- 특정한 종류의 표준 기능을 제공하기 위한 프로토콜 순응(conform) : 프로토콜을 채택할 수 있다.
Class Only
- 상속( Inheritance) : 어떠한 클래스의 특성을 물려받을 수 있는 기능
- 타입 캐스팅 (Type Casting) : 런타임에 클래스 인스턴스의 타입을 확인
- 소멸자 (Deinitializers) : 할당된 자원을 해제 시킴
- 참조 카운트 (Reference Counting) : 클래스 인스턴스에 하나 이상의 참조가 가능
Struct Only
- 초기화시 프로퍼티를 선언할 수 있는 이니셜라이저를 자동으로 생성해 제공한다.
아래 예시에서 처럼 구조체에 width, height 프로퍼티만 선언했다면 자동으로 해당 프로퍼티에 값을 할당할 수 있는 이니셜라이저를 제공한다.
let vga = Resolution(width: 640, height: 480)
선언 구문
구조체와 클래스는 선언하는데에 비슷한 구문을 가지고 있다. 구조체는 structure, 클래스는 class 키워드를 사용하여 선언한다.
각각 이름 앞에 키워드를 붙여 정의할 수 있다.
스위프트에서 클래스와 구조체를 선언할때는 UpperCamelCase를 사용하여 이름들을 대문자로 시작하게 해야한다.
반대로 프로퍼티나 메서드는 lowerCamelCase 기법을 따라 이름이 소문자부터 시작해야 한다.
아래는 클래스와 구조체 선언 예이다.
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution() // 위 Resolution 구조체를 값으로 사용
var interlaced = false
var frameRate = 0.0
var name: String?
}
클래스와 구조체 인스턴스 (Class and Structure Instances)
클래스와 구조체 이름 뒤에 빈 괄호를 적으면 각각 인스턴스를 생성할 수 있다.
let someResolution = Resolution() // 구조체 인스턴스 생성
let someVideoMode = VideoMode() // 클래스 인스턴스 생성
프로퍼티 접근 (Accessing Properties)
점 문법을 통해 클래스, 구조체 인스턴스의 프로퍼티에 접근할 수 있다.
print("The width of someResolution is \(someResolution.width)")
// "The width of someResolution is 0" 이 출력
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)")
// "The width of someVideoMode is now 1280" 이 출력
구조체와 열거형은 값 타입 (Structures and Enumerations Are Value Types)
값 타입이라는 뜻은, 값 타입인 어떠한 것이 함수에서 상수나 변수에 전달될 때 그 값이 복사돼서 전달된다는 의미이다.
Swift에서 모든 구조체와 열거형은 값 타입이다.
값타입은 할당하는 순간 복사되기 때문에, 다른 변수에 어떠한 구조체 값을 할당 했다면 서로 같은 인스턴스가 아닌 다른 인스턴스이다.
아래는
또한 Resolution 구조체는 값 타입이기 때문에 resolution2의 프로퍼티에 다른 값을 대입한 후 , resolution1의 프로퍼티와 비교해도 다른값이 나오는걸 알 수 있다.
클래스는 참조 타입 (Classes Are Reference Types)
참조 타입은 변수나 상수에 값을 할당하거나 함수에 인자로 전달할 때, 그 값이 복사되지 않고 참조된다.
참조된다는 의미는 그 값을 가지고 있는 메모리를 바라보고 있다는 의미이다.
구조체 예시처럼 클래스의 인스턴스를 하나 만들고, 해당 인스턴스를 할당 받는 인스턴스를 만들어서 주소값을 비교해보자.
주소값이 동일하다는 것을 알 수 있다.
프로퍼티 변경 후 두 인스턴스가 동일하게 변경되었다는것을 확인할 수 있다.
얕은복사 vs 깊은복사
얕은복사 ( Shallow Copy )
객체를 복사할 때 , 최소한의 복사만 한다.
얕은복사를 하게되면 해당 객체의 인스턴스가 메모리에 새로 생성되지 않는다.
값 자체를 복사하는것이 아닌 주소값을 복사하여 같은 메모리를 가르키기 때문
새로운 인스턴스를 생성하지 않기 때문에 깊은복사보다 상대적으로 빠르다.
참조타입을 복사하는 경우 얕은 복사가 일어남.
그럼 swift의 class는 깊은복사를 할 수 없나요?
-> NScopying 프로토콜을 채택한 객체를 복사할 경우 새로운(독립적인) 메모리에
인스턴스를 복사시킬 수 있음.
깊은복사 ( Deep copy )
데이터 자체를 통쨰로 복사한다
복사된 두 객체는 완전히 독립적인 메모리를 차지한다.
value type의 객체들은 깊은복사를 하게 된다.
깊은복사를 하게된다면 복사된 객체와 원래 객체는 완전히 독립적이게 된다.
Reference type in Reference type
클래스안에 클래스 , 클래스가 NScopying을 준수하는 클래스여서 깊은복사가 됐었더라도 , 그 안의 클래스를 깊은복사하겠다는 명시를해주지 않으면 얕은복사가 일어난다.
Referece type in Value type
struct 안의 class는 얕은복사가 이루어질까? 깊은복사가 이루어질까?
1. struct A 의 class의 주소와 , structB의 class의 주소 비교
StructA - class :
StructB - class:
얕은복사가 이루어졌다는것을 알 수 있다.
2. structA의 구조체 주소와 , structB의 구조체 주소 비교
1. structA - struct:
2. structB - struct:
깊은복사가 이루어졌다는것을 볼수 있다.
자 이제 class는 명시해주지 않는 한 기본적으로 얕은복사가 이루어지고 , struct는 값복사가 이루어진다는것을 알았다.
Value type in Referece type
classA와 classB 주소
classA 의 Number 주소와 classB의 Number 주소 비교
이렇게 구조체임에도 주소가 복사가 되는 이유는 구조체를 복사하기 이전에 classB는 classA를 복사했기 때문이다.
여기서 classB가 복사한건 classA의 인스턴스를 가르키는 주소값이다 (인스턴스 자체가 아니다) 실제로 classA는 인스턴스 자체가 아닌 그 인스턴스를 가리키고 있는 주소값이기 때문이다.
따라서 classB의 구조체를 접근하려하면 그 주소가 가리키는 메모리( 실제 classA가 할당되어있는 메모리 ) 에 접근하여 구조체를 접근한다. 그렇기 때문에 classA와 classB의 구조체 주소값이 같은것이다.