[swift] 옵셔널

orzr·2023년 4월 7일
0

옵셔널

옵셔널은 값이 있을 수도, 없을 수도 있는 표현이다. 즉, 값이 nil일 수도 있다는 것을 의미한다.

public enum Optiona<Wrapped> : ExpressibleByNilLiteral {
	case none
    case some(Wrapped)
    public init(_ some: Wrapped)
    // 중략...
}

옵셔널은 이처럼 enum으로 구현되어 있다. nil일 때는 none 케이스가 되고 값이 있는 경우는 some 케이스가 된다. 이 때 연관값으로 Wrapped가 있는데, 값이 있는 경우 Wrapped에 값이 할당된다는 의미이다. 즉, 값이 옵셔널이라는 열거형에 보호되어 래핑되어 있는 모습이다.

옵셔널 추출

swift는 타입에 엄격한 언어이다. 때문에 Int 타입과 Int? 타입은 완전히 다른 타입으로 인식한다. 옵셔널로 래핑된 데이터를 사용하기 위해서는 언래핑 과정이 필요하다.

  1. 옵셔널 바인딩
  2. 옵셔널 체이닝
  3. nil 병합 연산자
  4. 암시적 추출
  5. 강제 추출

옵셔널 바인딩

swift 문법에서 지향하는 옵셔널 추출 방식이다. 옵셔널에 값이 있는지 확인하여 값이 있다면 옵셔널에서 값을 추출한다. 추출된 값은 옵셔널이 아닌 형태로 상수나 변수로 할당되며 일정 블록 안에서 사용할 수 있다.

if let 상수명 = 옵셔널 상수(혹은 변수) {
	실행 구문
}

let을 사용한 상수가 아닌 var을 사용하여 변수로 할당할 수도 있다. 또한, 쉼표를 이용하여 한번에 여러 옵셔널 값을 바인딩 할 수 있으며 이때 하나라도 값이 nil일 경우 실행 구문은 실행되지 않는다.

guard let 상수명 = 옵셔널 상수 else { 제어문 전환 명령어 }

guard 구문은 return, continue, break, throw 등의 제어문 전환 명령어를 사용할 수 있는 함수나 메서드, 반복문 등의 특정 블록 내에서만 사용할 수 있다. 때문에 위와 같은 표현은 옵셔널 값이 있는 경우 값을 추출하여 함수내에서 사용하겠다는 의미고, 만약 값이 없다면 더이상 해당 함수를 실행하지 않고 끝내겠다는 의미이다.

옵셔널 체이닝

체이닝이라는 말 그대로 옵셔널이 자전거 체인처럼 서로 꼬리를 물고 있는 형태로, 보통 중첩된 형태를 표현할 때 사용된다.

Class Room {
	var number: Int
    
    init(number: Int) {
    	self.number = number
    }
}

Class Building {
	var name: String
    var roomt: Room?
    
    init(name: String) {
    	self.name = name
    }
}

struct Address {
	var province: String
    var city: String
    var street: String
    var building: Building?
    var detailAddress: String?
}

Class Person {
	var name: String
    var address: Address?
    
    init(name: String) {
    	self.name = name
    }
}

위와 같이 설계가 된 상황이다.

let orzr: Person = Pserson(name: "orzr")

let orzrRoom: Int? = orzr.address?.building?.room?.number	//	nil

orzr의 방 번호에 접근하기 위해 ?를 사용하여 꼬리에 꼬리를 물어 옵셔널 값을 추출했다. .address?.building?.room?.number 중 하나라도 nil 값이 반환되면 결과값으로 nil을 반환하게 된다.

nil 병합 연산자

A ?? B 로 표현하며 A가 nil이 아니라면 A를 반환하고 A가 nil이라면 B를 반환한다.

let value: Int = someValue ?? 0

암시적 추출

nil값을 사용해야하지만 매번 바인딩을 통해 추출하는게 귀찮을 때, nil을 할당해줄 수 있으면서 옵셔널이 아닌 상수나 변수를 사용하면 좋을 것이다. 이때 타입 뒤에 !를 붙여 암시적 추출 방법을 사용하면 된다.

iOS 개발을 하다보면 아래와 같은 표현을 자주 접하게 된다.

@IBOutlet weak var nameLabel: UILabel!

암시적 추출로 지정된 타입은 일반 값처럼 사용할 수 있으며 여전히 옵셔널이기 때문에 nil값도 할당해줄 수 있다.

프로퍼티에 유효한 값이 들어가야 클래스나 구조체가 초기화 되는데, 뷰 컨트롤러가 인스턴스화될 때 뷰는 아직 로드되지 않은 상태이다. 그래서 초기화가 되는 동안 프로퍼티에 nil이 들어가기 위해 암시적 추출 옵셔널을 사용한다. nameLabel은 nil상태지만 런타임시 스토리보드와 연결 된다.

강제 추출

옵셔널 값을 추출하는 가장 단순하지만 가장 위험한 방법으로, 옵셔널을 사용하는 의미가 무색해지는 방법이다.

var name: String? = "orzr"

var my: String = name!

name = nil

my = name!	// 런타임 오류

강제 추출을 하는 방법은 옵셔널 변수(혹은 상수) 뒤에 !를 붙이면 된다. 하지만 값이 nil일 때 강제 추출을 진행하게 되면 런타임 오류가 발생하니 강제 추출 방식은 지양하자!!

profile

0개의 댓글