Class
- swift에서는 객체라는 용어 대신에
인스턴스
라는 용어를 사용합니다. 한마디로 클래스 타입의 인스턴스를 객체라고 칭하지 않습니다.- 단일 상속만 가능합니다.
- (인스턴스/타입) 메서드, (인스턴스/타입) 프로퍼티 (
Struct
와 같음)- 참조타입(=reference type) (리퍼런스
reference
라고 부름)- iOS 프레임워크의 대부분이
클래스
로 구성되어있습니다.- SwiftUI에서는 대부분잉
Struct
로 구성되어있습니다.
class
는 프로퍼티와 메서드를 사용하여 새로운 유형을 생성할 수 있다는 점에서 구조체와 유사하지만, 중요한 차이점이 있으며 각 차이점을 하나씩 살펴보겠습니다.
클래스 정의
키워드 class
class 클래스 이름 {
// 프로퍼티와 메서드
}
sturct
구조와 비슷하죠? 차이점은 클래스는 상속받을 수 있기 때문에 상속 받을 때는 이름 뒤에 콜론( :
) 하고 부모클래스 이름을 명시해야 해요.
class 클래스 이름: 부모클래스 이름 {
// 프로퍼티와 메서드
}
class Person {
var height: Double = 0.0
var weight: Double = 0.0
}
여기서 정의된 클래스는 Double
타입인 height
와 weight
저장 프로퍼티가 있는 Person 클래스 이겠네요 :)
클래스 인스턴스의 생성과 초기화
클래스를 정의하고 초기화 할때는 따로 초깃값을 주지 않고 이니셜라이저를 사용하면 됩니다.
인스턴스가 생성되고 초기화 즉, 이니셜라이즈된 후에 구조체와 마찬가지로 마침표를 사용하면 되는데, 차이점은 struct
는 변수일때만 수정이 가능했지만, class
는 var
이건 let
이건 내부 프로퍼티 값을 변경할 수 있습니다,
class Character {
var name: String = "포뇨"
var isfavorite: Bool = false
}
let Mycharacter: Character = Character()
Mycharacter.name = "소피아"
Mycharacter.isfavorite = false
if Mycharacter.isfavorite {
print("내가 좋아하는 인형은 \(Mycharacter.name) 입니다")
} else {
print("나는 이 캐릭터를 좋아하지 않는다.")
} // 나는 이 캐릭터를 좋아하지 않는다.
var myCharacter: Character = Character()
myCharacter.name = "캘시퍼"
myCharacter.isfavorite = true
if myCharacter.isfavorite {
print("내가 좋아하는 인형은 \(myCharacter.name) 입니다")
} else {
print("나는 이 캐릭터를 좋아하지 않는다.")
} // 내가 좋아하는 인형은 캘시퍼 입니다
class와 struct의 차이점
구조체와 클래스는 서로 비슷하거나 같은 점이 많습니다. 먼저 같은점 부터 고고
같은 점
- 값을 저장하기 위해 프로퍼티를 정의할 수 있음
- 기능 실행을 위해 메서드를 정의할 수 있음
- 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값에 접근하도록 서브스크립트를 정의할 수 있음
- 이니셜라이저를 정의할 수 있음
- 익스텐션을 통해 확장할 수 있음
- 특정 프로토콜을 준수할 수 있음
다른 점
- 구조체는 상속할 수 없음
- 타입캐스팅은 클래스의 인스턴스에만 허용됨
- 디이니셜라이저는 클래스의 인스턴스에만 활용할 수 있음
- 참조 횟수 계산은 클래스의 인스턴스에만 적용됨
이 둘은 정의하는 방법, 인스턴스 하는방법, 프로퍼티와 메서드를 갖는다는 것 모두 비슷하지만, 이 두 타입을 구분 짓는 가장 큰 차이점은 클래스는 참조타입 이고 구조체는 값 타입! 그렇기에 참조 횟수 계산은 클래스에만 적용됩니다.
클래스에는 memberwise 이니셜 라이저
가 함께 제공되지 않습니다. 즉, 클래스에 프로퍼티가 있는 경우 항상 고유 한 이니셜 라이저를 만들어야 합니다.
예를 들면 다음과 같습니다.
class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
해당 클래스의 인스턴스를 만드는 것은 마치 구조체인 것처럼 보이죠?
let poppy = Dog(name: "Poppy", breed: "Poodle")
기존 클래스를 기반으로 클래스를 만들 수 있다는 것입니다. 원래 클래스의 모든 프로퍼티와 메서드를 상속하고 자체 클래스를 맨 위에 추가할 수 있습니다.
이를 클래스 상속(
class inheritance
) 또는 서브클래싱(subclassing
) 이라고하며 상속된 클래스를 "parent
” 또는 “super
" 클래스라고 하며 새 클래스를 "child
" 클래스라고 합니다.
class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
우리는 Poddle
이라는 class
을 바탕으로 새로운 class
를 만들 수 있습니다. 기본적으로 Dog
와 동일한 프로퍼티 및 이니셜 라이저를 상속합니다.
class Poodle: Dog {
}
하지만 이 클래스에 Poodle
만의 이니셜 라이저도 줄 수 있습니다. 우리는 그것이 항상 "푸들"이라는 품종을 가지고 있다는 것을 알고 있기 때문에 name
프로퍼티만 필요한 새로운 이니셜 라이저를 만들 수 있습니다.
더 좋은 점은 Poodle
이니셜 라이저가 Dog
이니셜 라이저를 직접 호출하도록 하여 동일한 설정이 가능하도록 할 수 있다는 점입니다.
class Poodle: Dog {
init(name: String) {
super.init(name: name, breed: "Poodle")
}
}
Swift는 항상 안전을 추구하기 때문에 자식 클래스의 super.init()
를 호출합니다. 왜냐하면 부모 클래스가 생성될 때 중요한 작업을 수행할 경우를 대비해서입니다.
예시
class Shape {
var sides: Int
init(sides: Int) {
self.sides = sides
}
}
class Rectangle: Shape {
var color: String
init(color: String) {
self.color = color
super.init(sides: 4)
}
}
- 클래스에는 합성된
memberwise 이니셜 라이저
가 제공되지 않습니다.- 한 클래스는 다른 클래스 위에 빌드("상속")하여 프로퍼티와 메서드를 얻을 수 있습니다.
- 구조체의 복사본은 항상 고유 한 반면 클래스 복사본은 실제로 동일한 공유 데이터를 가리 킵니다.
- 클래스에는 클래스의 인스턴스가 소멸될 때 호출되는 메서드 인
deinitializer
가 있지만 구조체는 그렇지 않습니다.- 상수 클래스의 변수 속성은 자유롭게 수정할 수 있지만 상수 구조체의 변수 속성은 수정할 수 없습니다.
이러한 차이점을 곧 자세히 설명할 것이지만 요점은 이들이 존재하고 중요 하다는 것입니다.
대부분의 Swift 개발자는 가능하면 클래스보다 구조체를 사용하는 것을 선호합니다.
즉, 구조체 대신 클래스를 선택할 때 위의 동작 중 하나를 원하기 때문에 그렇게 하는 것입니다.
값 타입 참조 타입
구조체 = 값 타입 / 클래스 = 참조 타입.
값 타임 과 참조 타입의 가장 큰 차이점은 ' 무엇이 전달되느냐 ' 입니다.
만약 어떠한 함수의 전달인자로 값 타입의 값을 넘기면 전달인자 값이 복사되어 전달 되겠죠?
근데, 참조 타입이 전달인자로 전달 될땐 값을 복사하지 않고 참조가 전달 될 뿐!
struct information {
var name: String
var age: Int
}
var SeogunInfo: information = information(name: "서근", age: 26)
SeogunInfo.age = 27
var MijinInfo: information = SeogunInfo
//MijinInfo가 SeogunInfo의 값을 복사하여 할당했음
print("Seogun age : \(SeogunInfo.age)")
print("Mijin age : \(MijinInfo.age)")
/*
Seogun age : 27
Mijin age : 27
*/
// 만약 MijinInfo의 이름을 변경한다면?
MijinInfo.name = "미진"
print("Seogun name: \(SeogunInfo.name)") //Seogun 이름값은 변동이 없음
print("Mijin name: \(MijinInfo.name)") // 복사해서 가져왔기 때문에 별개의 값을 갖음.
/*
Seogun name: 서근
Mijin name: 미진
*/
/* ================== */
class Person {
var name: String = "서근"
var height: Double = 0.0
}
let Seogun: Person = Person()
let Mijin: Person = Seogun // 미진은 서근의 참조를 할당했음
print("Seogun의 height: \(Seogun.height)")
print("Mijin의 height: \(Mijin.height)")
/*
Seogun의 height: 0.0
Mijin의 height: 0.0
*/
Mijin.height = 170
// Seogun은 Mijin을 참조하기 때문에 값이 같이 변동됨
// 즉, Seogun과 Mijin은 참조하는 곳이 같음을 알수 있음.
print("Seogun의 height: \(Seogun.height)")
print("Mijin의 height: \(Mijin.height)")
/*
Seogun의 height: 170.0
Mijin의 height: 170.0
*/
값 타입(구조체) : 값 타입 데이터 -> 함수의 전달인자로 전달 -> 인스턴스 생성됨 -> 복사되어 들어감
참조 타입(클래스) : 참조타입 데이터 -> 기존 인스턴스의 참조를 전달
식별 연산자
클래스의 인스턴스끼리 참조가 같은지 확인 할때는 식별 연산자 를 사용!
class Person {
var name: String = "서근"
var age: Int = 26
}
let Seogun: Person = Person()
let Mijin: Person = Seogun // Seogun의 참조를 할당
let anotherFrnd: Person = Person() // 새로운 인스턴스 생성
print(Seogun === Mijin) // true
print(Mijin === anotherFrnd) // false
print(Mijin !== anotherFrnd) // true
TEST : 문제를 풀려면 이곳을 클릭해주세요.
읽어주셔서 감사합니다🤟
'SWIFT > Grammar' 카테고리의 다른 글
Swift : 기초문법 [CGSize와 CGRect의 차이점과 CGPoint] (0) | 2021.04.04 |
---|---|
Swift : 기초문법 [Unwrapping with guard] (0) | 2021.03.02 |
Swift : 기초문법 [프로퍼티 #4-1 Static] (0) | 2021.03.01 |
Swift : 기초문법 [기본 연산자] (0) | 2021.03.01 |
swift : 기초문법 [ 프로퍼티 #1-1 지연 저장 프로퍼티(Lazy)] (0) | 2021.03.01 |