반응형
참고 사이트: iOS 프로그래밍을 위한 스위프트 기초(edwith)
참고 문서: The Swift Language Guide (한국어)
실행 환경: Xcode 12 실행 → Create a new Xcode project → macOS → Command Line Tool 선택 → Product Name, Organization Identifier 입력 후 프로젝트 생성
목차
- 스위프트(Swift) 5 배워보기 - 1. 다양한 타입
- 스위프트(Swift) 5 배워보기 - 2. 함수, 제어문, 옵셔널
- 스위프트(Swift) 5 배워보기 - 3. 클래스, 구조체, 열거형
- 스위프트(Swift) 5 배워보기 - 4. 클로저
- 스위프트(Swift) 5 배워보기 - 5. 프로퍼티
- 스위프트(Swift) 5 배워보기 - 6. 상속, 인스턴스 생성/소멸
- (계속)
클래스와 구조체
- 클래스와 구조체는 객체 지향 프로그램(OOP)을 위한 필요한 요소들이다.
- 코드를 한 묶음(클래스/구조체)으로 만들어 다양하게 활용할 수 있다.
- 여기서는 클래스와 구조체의 기본 문법과 비교점만 다룬다.
클래스와 구조체의 비교
아래에 언급되는 다양한 정보는 추후 자세히 다룰 예정
공통점
- 값을 저장하기 위한 프로퍼티(Properties)를 정의할 수 있음
- 기능을 제공하기 위한 메소드(Methods)를 정의할 수 있음
- 초기 상태를 설정할 수 있는 이니셜라이저(Initializer)를 정의할 수 있음
- 기본 구현상태에서 기능 확장할 수 있음
- 특정한 종류의 표준 기능을 제공하기 위한 프로토콜(Protocols)을 따름
클래스에서만 가능한 기능
- 상속(Inheritance): 클래스의 여러 속성을 다른 클래스에서 사용할 수 있음
- 타입 캐스팅(Type casting): 런타임 시점에 클래스 인스턴스의 타입을 확인함
- 소멸자(Deinitializers): 할당된 자원을 해제시킴
- 참조 카운트(Reference counting): 클래스 인스턴스에 하나 이상의 참조가 가능
기본 형태
- 클래스는 class 키워드를 사용
- 구조체는 struct 키워드를 사용
- 변수/상수를 선언할 때와는 다르게 클래스/구조체 선언 시에는 대문자로 시작(UpperCamelCase)한다.
// 구조체 선언
struct Resolution {
// 프로퍼티 선언
var width = 0 // 타입을 지정하지 않았지만 초기 값으로 0을 할당 했기 때문에,
var height = 0 // 타입추론에 의해 자동으로 Int 타입을 가지게 됨
}
// 클래스 선언
class VideoMode {
// 프로터피 선언
var resolution = Resolution() // 구조체를 값으로 사용할 수 있다.
var interlaced = false
var frameRate = 0.0
var name: String? // 옵셔널
}
인스턴스
- 클래스와 구조체를 선언 후, 이를 사용하기 위해서는 인스턴스를 생성해야 한다.
- 클래스와 구조체 이름 뒤에 빈 괄호()를 붙이면 인스턴스가 생성된다.
// 구조체 인스턴스 생성
let someResolution = Resolution()
// 클래스 인스턴스 생성
let someVideoMode = VideoMode()
프로퍼티 접근
- 생성된 인스턴스의 프로퍼티에 접근 및 값 할당을 할 수 있다.
// 클랙스/구조체 인스턴스에 점(.)을 붙여 안에 있는 프로퍼티에 접근할 수 있다.
// Resolution 구조체의 width 프로퍼티에 접근
print("someResolution의 가로 길이는 \(someResolution.width)이다.")
// someResolution의 가로 길이는 0이다. 가 출력
// VideoMode 클래스 안에 값으로 사용된 Resolution 구조체 안의 프로퍼티에 접근
print("someVideoMode의 가로 길이는 \(someVideoMode.resolution.width)이다.")
// someVideoMode의 가로 길이는 0이다. 가 출력
// 값을 할당 할 때에는 프로퍼티에 접근 후 원하는 값을 할당하면 된다.
someVideoMode.resolution.width = 1280
print("someVideoMode의 현재 가로 길이는 \(someVideoMode.resolution.width)이다.")
// someVideoMode의 현재 가로 길이는 1280이다.
클래스와 구조체의 타입
- 구조체는 값(value) 타입이다. 이는 함수에서 상수나 변수로 값이 전달될 때 값 그대로 복사된다는 뜻이다.
// Resolution 구조체의 인스턴스(hd) 생성
let hd = Resolution(width: 1920, height: 1080)
// 생성된 인스턴스 hd를 변수 cinema에 할당
var cinema = hd
// cinema의 width 프로퍼티 값 변경
cinema.width = 2048
// hd와 cinema의 width 프로퍼티 확인
print("cinema의 현재 가로 길이 \(cinema.width)")
print("hd의 가로 길이 \(hd.width)")
// cinema의 현재 가로 길이 2048
// hd의 가로 길이 1920
// 서로의 값이 다른 것을 확인할 수 있다.
// hd가 cinema에 할당되는 순간 복사되었기 때문에,
// cinema와 hd는 서로 다른 인스턴스가 된다.
- 반면, 클래스는 참조(Reference) 타입이다. 변수나 상수에 값을 할당하거나 함수에 인자로 전달할 때, 값이 복사되는 것이 아닌 값이 저장되어 있는 주소만 복사(참조)된다.
- 클래스는 참고 타입이기 때문에, 상수와 변수가 같은 인스턴스를 참조하고 있는지 비교(식별 연사자)할 수 있다.
- === : 두 상수나 변수가 같은 인스턴스를 참조하고 있는 경우에 true
- !== : 두 상수나 변수가 다른 인스턴스를 참조하고 있는 경우에 true
// VideoMode 클래스의 tenEighty 인스턴스 생성 후 각 프로퍼티에 값 할당
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
// anotherTenEighty 상수를 하나 만들고, tenEighty 인스턴스를 할당
// anotherTenEighty의 frameRate 프로퍼티의 값을 변경
let anotherTenEighty = tenEighty
anotherTenEighty.frameRate = 30.0
// tenEighty 인스턴스와 anotherTenEighty 인스턴스의 frameRate 프로퍼티 값 확인
print("tenEighty의 frameRate 값은 \(tenEighty.frameRate)")
print("anotherTenEighty의 frameRate 값은 \(anotherTenEighty.frameRate)")
// tenEighty의 frameRate 값은 30.0
// anotherTenEighty의 frameRate 값은 30.0
// 여기서 보면, 처음에 tenEighty.frameRate에서 할당한 25.0이 아니라
// anotherTenEighty.frameRate에서 할당한 30.0으로 값이 출력되는 것을 볼 수 있다.
// 이는 anotherTenEighty 상수가 tenEighty의 값을 복사한 것이 아니라,
// 바라보고 있는 tenEighty의 값을 변경했기 때문이다.
그래서, 언제 클래스를 사용하고 언제 구조체를 사용해야 하나?
일반적으로 아래 조건 중 1개 이상을 만족하면 구조체를 사용하는 것을 고려해볼 수 있다.
- 간단한 값을 캡슐화(encapsulate)하기 위한 경우
- 인스턴스 또는 프로퍼티가 참조되기 보다 그대로 복사되기를 원하는 경우
- 프로퍼티나 메소드 등을 상속할 필요가 없는 경우
위 경우를 제외하고는 클래스를 사용하면 된다.
열거형
- 열거형은 유사한 종류의 여러 값을 한 곳에 모아서 정의한 것(예, 요일, 계절 등)이다.
- 스위프트의 열거형은 C나 Objective-C와 다르게 Integer뿐만 아니라 string, character, floating 값들을 사용할 수 있다.
- 클래스/구조체와 마찬가지로 선언 시에는 대문자로 시작(UpperCamelCase)한다.
- 단, enum 내의 각 case는 소문자로 시작(LowerCamelCase)한다.
기본 형태
enum CompassPoint {
case north // ≠ 0 스위프트는 타 언어와 다르게 내부적으로 정수값을 가지지 않는다.
case south // ≠ 1
case east // ≠ 2
case west // ≠ 3
}
// 여러 case를 콤마(,)로 한줄로 적을 수도 있다.
enum Planet {
case mercury, venus, earth
}
열거형의 사용
- 각 열거형의 값을 Switch 문에서 매칭할 수 있다.
// 열거형의 값을 새로운 타입으로 할당할 수 있다.
// CompassPoint의 west를 directionToHead 변수에 할당
var directionToHead = CompassPoint.west
// directionToHead에 CompassPoint 타입으로 한번 정의되고 나면,
// 다음에 다른 값을 할당 할 때는 CompassPoint를 생략할 수 있다.
directionToHead = .south
// Switch 구문으로 열거형 값 매칭
// 열거형의 모든 case를 포함해야 한다. 이 경우 default를 생략할 수 있다.
// 만약 모든 case를 포함하지 않는다면, default를 적어 빠진 case가 없도록 해야 한다.
switch directionToHead {
case .north:
print("북쪽입니다.")
case .south:
print("남쪽입니다.")
case .east:
print("동쪽입니다.")
case .west:
print("서쪽입니다.")
}
// 남쪽입니다. 가 출력된다.
Raw 값
- 앞서, 스위프트의 열거형은 각각의 case에 정수 값을 할당하지 않는다고 했다.
- 그러나 Raw 값 지정을 통해 각 case에 Integer를 비롯하여 String, Character 등의 값을 할당할 수 있다.
- Integer 값을 지정한 경우 은 C 언어의 enum과 마찬가지로 자동으로 1씩 증가한다.
// 열거형의 case에 Character 형을 정의한 경우
// 단, Raw 값은 중복되면 안된다.
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
// Integer 값을 정의한 경우
enum Planet: Int {
case mercury = 1 // mercury에만 명시적으로 1을 할당함.
case venus
case earth
}
// 위에서 보면 venus와 earth에는 명시적으로 값을 할당하지 않았지만,
// 자동으로 mercury에서 값이 증가된 2와 3이 할당된다.
Raw 값을 통한 초기화
- rawValue를 통해 열거형 변수를 초기화할 수 있다.
- Raw 값을 통한 초기화는, 열거형에 지정된 Raw 값이 없을 경우 초기화에 실패하여 nil이 된다.
- 모든 raw값에 대해 열거형 case 반환이 보장되지 않으므로 실패할 수 있는 초기자(failable initializer)이다.
- rawValue를 통해 초기화한 인스턴스는 옵셔널 타입을 가진다.
// 예를 들어 아래와 같이 열거형을 생성했다고 가정한다.
enum Planet: Int {
case mercury = 0
case venus = 1
case earth = 2
}
// 값이 존재하는 earth를 열거형 변수의 초기 값으로 지정하면
// 정상적으로 초기 값이 지정된다.
let possiblePlanet = Planet(rawValue: 2)
print(possiblePlanet)
// Optional(test.Planet.earth) 이 출력됨.
// 만약, 존재하지 않는 값(rawValue: 3)을 초기 값으로 지정하면
let possiblePlanet = Planet(rawValue: 3)
print(possiblePlanet)
// nil 이 출력 됨.
// 따라서 옵셔널 변수 처리할 때와 같이
// if let 문을 사용해서 처리하면 된다.
if let somePlanet = Planet(rawValue: 3) {
switch somePlanet {
case .earth:
print("지구입니다.")
default:
print("지구가 아닙니다.")
}
} else {
print("해당 위치의 행성이 존재하지 않습니다.")
}
// 해당 위치의 행성이 존재하지 않습니다. 이 출력 됨.
반응형
'IT' 카테고리의 다른 글
스위프트(Swift) 5 배워보기 - 4. 클로저 (0) | 2021.06.07 |
---|---|
[언리얼4] C++ 컴파일이 느린 경우 해결 방법 중 1개 (0) | 2021.06.02 |
스위프트(Swift) 5 배워보기 - 2. 함수, 제어문, 옵셔널 (0) | 2020.10.06 |
스위프트(Swift) 5 배워보기 - 1. 다양한 타입 (2) | 2020.10.05 |
파워목업(PowerMockup) 체험판 간단 사용기 (0) | 2020.10.04 |
최근댓글