구조체와 클라스
- 구초제와 클라스는 하나의 큰 코드 블록이다. 이 안에 변수나 상수들을 넣어서 기능을 정의 할 수 있다.
- 구조체와 클라스는 아래와 같은 부분에서 유사하고 다르다.
공통점
- 프로퍼티 : 변수나 상수를 사용하여 값을 저장하는 프로퍼티를 정의 할 수 있다.
- 메서드 : 함수를 사용하여 기능을 제공하는 메서드를 정의 할 수 있다.
- 서브 스크립트: 속성값에 접근할 수 있는 방법을 제공하는 서브스크립트를 정의 할 수 있다.
- 초기화 블록: 객체를 원하는 초기 상태로 설정해주는 초기화 블록을 정의할 수 있다.
- 확장: 객체에 함수적 기능을 추가하는 확장 구문을 사용할 수 있다.
- 프로토콜: 특정 형식의 함수적 표준을 제공하기 위한 프로토콜을 구현할 수 있다.
차이점 (클래스의 장점)
- 상속 : 클래스의 특성을 다른 클래스에게 물려줄 수 있다. (구조체 X)
- 타입 캐스팅: 실행 히 컴파일러가 클래스 인스턴스의 타입을 미리 파악하고 검사할 수 있다.
- 소멸화 구문 : 인스턴스가 소멸되지 직전에 처리해야 할 구문을 미리 등록해 놓을 수 있다.
- 참조에 의한 전달 : 클래스 인스턴스가 전달될 때에는 참조 형식으로 제공되며, 이때 참조가 가능한 개수는 제약이 없다.
정의 구문
- Camel 표기법의 따라서 대문자로 시작하고, 두 단어 이상 연결 될때는 대문자로 구분한다.
struct Rectangle {
}
class Shape {
}
메서드와 프로퍼티
- 구조체와 클래스의 내부적으로 정의한 변수나 상수는 프로퍼티라고 부르며, 내부적으로 정의한 함수는 메서드라고 부른다.
struct Rectangle {
var length = 0
var width = 0
func print(){
Sprint("It is a Rectangle")
}
}
class Shape {
var length = 0
var wdith = 0
var name : String?
func print(){
if let name = self.name {
print("It is a \(name)")
} else {
print("Name is not set")
}
}
}
인스턴스
- 구조체와 클래스를 생성했으면 이제 사용하는 방법은 인스턴스를 생성하는 것이다.
- 구조체와 클래스는 bluePrint라고 생각하면 되고, 인스턴스 생성을 통해 해당 메서드와 프로퍼티을 사용할 수 있는 것이다.
let rect1 = Rectangle()
print(rect1.width)
let shape1 = Shape()
shape1.name = "Circle"
shape1.print()
구조체 초기화
- 구조체인 경우 인스턴스를 생성할때 괄호안에 프로퍼티를 정의 해줄 수 있다
- 옵셔널 타입으로 선언되지 않은 프로퍼티는 무조건 초기화를 시켜줘야한다, 아래 두 경우를 통해 초기화를 할 수 있다.
- 프로퍼티를 선언하면서 동시에 초기값을 지정하는 경우, 혹은 초기화 메서드 내에서 프로퍼티의 초기값을 지정하는 경우
- 괄호 안에 프로퍼티를 초기화 해주는 구문은 멤버와이즈 초기화 구문 (Memberwise Initializer)라고 한다. 클래스는 해당 구문이 제공되지 않는다.
- 멤버와이즈 초기화 구문은 실제 구조체에서 정의된 프로퍼티 순서대로 대입을 해야한다.
var rect2 = Rectangle(width: 100, length: 100)
var rect2 = Rectangle(length: 100, width: 100)
클라스 초기화
- 클라스 인 경우 멤버와이즈 초기화 구문이 제공되지 않는다
- 따라서 클래스를 정의 할때 모든 프로퍼티를 초기화를 하던가, 옵셔널 타입으로 선언하는 방법이 있다.
- 다시 말해 클라스는 구조체와 다르게 인스턴스 생성과 동시에 프로퍼티 초기화 기능을 제공하지 않는다.
구조체의 값 전달 방식 : 복사
- 아래와 같은 코드가 있다고 가정해보자. 마지막 print 문에서는 100이 나올까 500이 나올까? 정답은 100이다. 구조체 인스턴스를 변수에 대입하면 기존 인스턴스의 복사본이 대입이 되는거기 때문에, 프로퍼티 값만 전달되는것이라고 생각하면 된다.
var rect2 = Rectangle(length: 100, width: 100)
var temp = rect2
rect2.width = 500
print(temp.width)
클래스의 값 전달 방식 : 참조
- 구조체와 다르게 클래스는 값 전달을 참조 방식으로 전달한다. 즉 주소값을 전달한다고 생각하면 된다.
var shape1 = Shape()
var temp = shape1
shape1.width = 500
print(temp.width)
temp.width = 1000
print(shape1.width)
- 구조체와 다른점이 또 하나 큰게 있는데, 구조체에서 인스턴스를 상수에 할당하면, 해당 인스턴스의 프로퍼티 또한 변경이 불가능한데, 클래스는 가능하다. 이 이유는 값 전달 방식의 차이, 참조 vs 값(복사) 때문이다.
- 클래스는 참조 타입이기 떄문에 상수에 할당 되더라도 실제 인스턴스는 힙(Heap)에 저장되고, 상수는 힙에 저장된 인스턴스를 가리키는 포인터 역할을 한다. 즉 우리는 상수에 저장된 클래스 인스턴스의 주소값을 가리키는 포인터로 값을 변경하는 것이다.
- 구조체는 반대로 값 타입이기 떄문에 해당 인스턴의 모든 프로퍼티도 변경할 수 없게 된다. 구조체의 인스터는 힙이 아닌 스택(Stack)에 저장된다.
- 힙(Heap)은 메모리의 한 영역으로, 동적으로 할당된 데이터를 저장하는데 사용하고, 주로 객체 (인스턴스)를 저장하기 위한 메모리 공간이며, 클래스로 생성된 인스턴스는 힙에 올라간다.
- 스택(Stack)은 메모리의 다른 영역으로 지역 변수와 함수 호출에 사용되는 데이터를 저장하는데 사용된다. 즉 구조체의 인스턴스는 값 타입으로 분류되기 때문에, 해당 인스턴스이 모든 데이터가 스택에 저장된다.
- stack과 heap의 차이는 자세히는 나중에 다시 공부하겠지만, 제일 큰 다른 점은 stack은 컴파일 시점에서 정적으로 크기가 정해지고, heap은 동적으로 메모리를 할당하게 된다. 완전 쉽게 생각하면, 구조체 인스턴스는 값, 데이터를 모두 stack에 저장하는 반명, 클래스 인스턴스는 주소값만 stack에 저장하고, 값은 heap에 저장한다. 따라서 상수로 인스턴스를 만들어도, stack에 있는 주소값만 상수로 저장된거 이기 때문에, 주소값만 못 바꾸는거지, 해당 주소값, 즉 heap에 있는 데이터는 변경 가능.