구조체와 클래스
같은점
- 프로퍼티, 메서드를 정의할 수 있음
- 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값에 접근하도록 서브스크립트를 정의할 수 있음
- 이니셜라이저를 정의할 수 있음
- 새로운 기능을 익스텐션을 통해 확장 할 수 있음
- 특정 프로토콜을 준수 할 수 있음
차이점
- 가장 큰 차이 : 구조체(값타입), 클래스(참조타입)
- 구조체는 상속을 할 수 없음
- 타입캐스팅은 클래스의 인스턴스에만 허용
- 디이니셜라이저(소멸자)는 클래스의 인스턴스에서만 활용가능
- 참조 횟수 계산(Reference Counting)은 클래스의 인스턴스에만 적용
프로퍼티, 메서드
프로퍼티
- 저장 프로퍼티 : 변수(var)/상수(let)
- 클래스의 경우 프로퍼티 기본값을 지정해주지 않으면 이니셜라이저를 반드시 정의 해줘야한다(구조체는 자동으로 생성)
- 연산 프로퍼티 : 상태에 따른 값을 연산하는 프로퍼티
- 접근자 : 인스턴스 내/외부의 값을 연산하여 적절한 값을 돌려줌
- 설정자 : 은닉화된 내부의 프로퍼티 값을 간접적으로 설정
- 사용하는 이유 : 외부에서 접근하려면 메서드를 두개를 생성해야하고, 생성하면 코드의 가독성이 나빠짐
struct CoordinatePoint {
var x: Int // 저장프로퍼티
var y: Int // 저장프로퍼티
var oppositePoint: CoordinatePoint { // 연산프로퍼티
get { // 접근자
return CoordinatePoint(x: -x, y: -y)
}
set(opposite) { // 설정자
x = -opposite.x
y = -opposite.y
}
}
}
- 프로퍼티 감시자 : 프로퍼티의 값이 변경됨에 따라 적절한 작업 가능
- 일반 저장 프로퍼티에 적용(지연X)
- 프로퍼티 재정의를 통해 상속받은 저장 프로퍼티 또는 연산 프로퍼티에도 적용 가능
class Person {
var name: String = "" {
willSet { print("이름이 \(name)에서 \(newValue)로 변경될 예정") }
didSet { print("이름이 \(oldValue)에서 \(name)로 변경됨") }
}
}
let A: Person = Person()
A.name = "Red"
- 타입 프로퍼티 : 각각의 인스턴스가 아닌 타입 자체에 속하는 프로퍼티
- 저장 타입 프로퍼티는 변수/상수 가능, 연산 타입 프로퍼티는 변수로만 선언
- 저장 타입 프로퍼티의 경우 반드시 초깃값을 설정해야하며, 지연연산됨, 다중 스레드 환경에서도 단 한 번만 초기화가되는 것을 보장 받음
class A {
static var typeProperty: Int = 0
static var typeComputedProperty: Int {
get { return typeProperty }
set { typeProperty = newValue }
}
}
A.typeProperty = 1234
print(A.typeProperty)
print(A.typeComputedProperty)
메서드
- 인스턴스 메서드
- 특정 타입의 인스턴스에 속한 함수
- 값타입에서 자신의 프로퍼티 값을 수정할때는 메서드 앞에 mutating 키워드를 붙여야한다
- self 프로퍼티 : 명확히 인스턴스를 지칭할 때 사용
class character { // 참조타입
var level: Int = 1
func lvlUp(){
self.level += 1
}
}
let human1: character = character()
human1.lvlUp()
struct character { // 값타입
var level: Int = 1
mutating func lvlUp(){
level += 1
}
mutating func jumpLvl(level: Int){
self.level = level
}
mutating func reset(){
self = character() // 새로운 인스턴스를 생성하여 인스턴스에 대입
}
}
var human2: character = character()
human2.jumpLvl(level: 100) // level 100
human2.lvlUp() // level 101
human2.reset() // level 1
- 타입 메서드
- 클래스 타입 메서드는 static과 class 키워드로 사용 가능(static으로 정의하면 상속 후 재정의가 불가능하고 class로 정의하면 상속 후 재정의가 가능하다)
- 참고 : 상속이 안되는게 아닌 상속후 재정의가 안되는 것
class A {
static func staticTypeMethod() {
print("A staticTypeMethod")
}
class func classTypeMethod() {
print("A classTypeMethod")
}
}
class B: A {
override static func staticTypeMethod() {
}
override class func classTypeMethod() {
print("B classTypeMethod")
}
}
접근제어
- open : 개방 접근 수준, 모듈 외부까지, 클래스에서만 사용, 공개수준과 비슷하지만 차이점이 존재, 대부분 다른 모듈에서도 부모클래스로 사용하겠다는 목적으로 설계
- 다른 모든 접근 수준의 클래스는 클래스가 정의된 모듈 안에서만 상속가능
- 다른 모든 접근 수준의 클래스 멤버는 멤버가 정의된 모듈 안에서만 재정의 가능
- 개방 접근 수준의 클래스는 정의된 모듈 밖에서도 상속가능
- 개방 접근 수준의 클래스 멤버는 정의된 모듈 밖에서도 재정의 가능
- public : 공개 접근 수준, 모듈 외부까지
- 프레임워크에서 외부와 연결될 인터페이스 구현에 많이 사용
- 스위프트 기본요소가 대부분 여기에 해당
- internal : 내부 접근 수준, 모듈 내부
- 기본적으로 모든 요소에 암묵적으로 지정하는 기본 접근 수준
- 모듈을 가져다 쓰는 외부 모듈에서는 접근을 할 수 없음
- fileprivate : 파일외부비공개 접근 수준, 파일 내부
- 소스파일 외부에서 값이 변경되거나 함수를 호출하면 부작용이 생길 경우 사용
- private : 비공개 접근 수준, 기능 정의 내부
- 기능을 정의하고 구현한 범위 내에서만 사용가능
- 심지어 같은 소스파일 안이어도 구현범위가 다르면 사용불가