32YB SOPT/swift 문법 스터디

[SOPT] 야우쓰_2주차 (옵셔널 삼항연산자)

신_이나 2023. 4. 30. 19:03

 

 

 

야우쓰란? "야 우리도 스위프트 할 수 있어"

in SOPT iOS

 

 

 

 

 

 

 

 

2주차의 키워드는 옵셔널 삼항연산자다.

 

 

 

- optional

 직역하면, optional 이란 wrapped 된 값이나 값의 부재를 나타낸다. 구체적으로 말하자면, 반환하고자 하는 값을 optinal 로 한 번 더 wrapped 하는 것을 의미한다. 왜냐하면 선언한 변수에 값이 없을 수도 있기 때문에, nil 값을 고려한 개념이라고 생각해줄 수 있다. 따라서 값이 있다면 값이 있다고 판단하고, 정상적인 변환이 불가능하다면 nil 값을 반환하고 프로그램이 종료된다.

++ 일반적인 변수, 상수에는 nil 값을 직접적으로 대입할 수 없다. 따라서 옵셔널 타입으로 설정해주어야 nil 대입이 가능하다.

var i = 0
i = nil
print(i)

//Prints "'nil' cannot be assigned to type 'Int'"
var i : Int?
i = nil
print(i)

//Prints "nill"

 

 

애플에서 보여주는 예시를 살펴보자!

let shortForm: Int? = Int("42")
let longForm: Optional<Int> = Int("42")

let number: Int? = Optional.some(42)
let noNumber: Int? = Optional.none
print(noNumber == nil)
// Prints "true"

 

++스터디 Question

var myBoyFriend: String? = "장석우"
print(myBoyFriend)

var mySecondBoyFriend: String? = nil
print(mySecondBoyFriend)

//예상 출력 "장석우 nill"
//실제 출력 "Optional("장석우") nil"

감싸주기 때문에 Optinal이 붙는구나~

 

 

 

- if let / guard let

if let

위 스터디 Question 의 실제 출력에서 나타나듯이, optional 을 설정한 변수는 출력도 optional 을 달고 나온다. 따라서 우리는 요 달려나오는 optional 을 벗겨주기 위해 if let 을 사용한다. 따라서 값이 있는 경우와 값이 없는 nil 의 경우를 체크한다.

var i : Int? = 1

if let j = i {
    print(j)
} else {
    print("NO")
}

//Prints "1"

위 코드를 보면 i 가 옵셔널로 선언되었지만 lf let 을 통해 j 에 변수를 다시 넣으면서 optional 을 벗겨 출력하도록 한다.

++ 왜 let 을 사용할까? - if let 안에서 변수는 변하지 않기 때문에 let 주로 사용한다. var 도 값은 똑같이 출력된다.

 

guard let

guard let 은 야우쓰 1주차에 정리한 바가 있다. 아래 코드 또한 전주차에 예시로 들었던 코드다.

for i in 0...3 {
    guard i == 1 else { continue }
    print(i)
}
//Print "1"

 guard 뒤에 쓰인 조건이 false 인 경우는 else 에 작성된 조건을 따르고 지나가며, true 일 경우엔 나머지도 모두 실행한다. 함수나 메서드, 반복문에서 사용하지 않으면 제어문 전환 명령어를 사용할 수 없기 때문에 guard 또한 사용할 수 없다. 제어문 전환 명령어로 return, break, throw 등을 사용할 수 있다.

 

if let 과 guard let의 차이점

  if let guard let
else 구문 필요 X O
제어문 전환 명령어 필요 X
변수의 범위 지역변수 지역변수 뿐만 아니라 전역변수 가능
     
  optional 값을 nil 확인에 용이 조건 확인하여 예외처리할 때 용이
     

 

 

 

- Optional 추출

옵셔널 강제 추출 (Force unwrapping)

말 그대로 옵셔널의 값을 강제 추출하여 반환하는 것으로 옵셔널 값의 뒤에 느낌표(!)를 붙여주면 강제로 추출 가능하다. 

런타임 오류가 날 가능성이 있다.

var i : Int? = 1
var j : Int = i!
i = nil
print(j)

//Prints "1"

 

 

옵셔널 바인딩 (Optional Binding)

옵셔널 강제 추출이 런타임 오류가 날 가능성이 있다고 하였다. 따라서 이 위험성을 보완하고자 옵셔널 바인딩으로 구현할 수 있다. 옵셔널에 값이 있는지 확인할 때 사용한다. 만약 값이 없다면 nil 값으로 선언된다. 이때 사용되는 것이 앞서 살펴보았던 if let 과, guard let 이다.

 

 

암시적 추출 옵셔널 방식 (Implicitly Unwrapped Optional)

암시적 추출 옵셔널 또한 느낌표(!)를 사용하여 선언한다. 하지만 강제 추출과 다른 점은 강제 추출은 반환하고자 하는 값에 느낌표를 붙인다면, 암시적 추출 옵셔널 방식은 선언할 때 타입 뒤에 물음표 대신 느낌표를 붙여 사용하는 것이다. 따라서 nil 을 할당하고자 하는 대상이 옵셔널이 아닌 변수나 상수를 사용할 때 이용된다. 

var jiwon: String! = "shin"
jiwon = nil

if let ji = jiwon {
    print(ji)
}else {
    print("nil")
}

위 코드를 보면 String 의 타입을 가졌어도 jiwon 이라는 변수에 nil 이 선언되는 것을 볼 수 있다.

 

++ 스터디 Question

 

//Force unwrapping
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: TvingSettingTableViewCell.cellIdentifier, for: indexPath) as? TvingSettingTableViewCell else { return UITableViewCell()}
            cell!.configureCell(settingDummy[indexPath.row])
            return cell!

//Optional Binding - guard let
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: TvingSettingTableViewCell.cellIdentifier, for: indexPath) as? TvingSettingTableViewCell else { return UITableViewCell()}
            cell.configureCell(settingDummy[indexPath.row])
            return cell

//Optional Binding - let if
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            if let cell = tableView.dequeueReusableCell(withIdentifier: TvingSettingTableViewCell.cellIdentifier, for: indexPath) as? TvingSettingTableViewCell {
            	cell.configureCell(settingDummy[indexPath.row])
            	return cell
            }
            else { 
            	return UITableViewCell() 
            }
            
//Implicitly Unwrapped Optional
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell : tableView!=dequeueReusableCell(withIdentifier: TvingSettingTableViewCell.cellIdentifier, for: indexPath) as? TvingSettingTableViewCell else { return UITableViewCell()}
            cell.configureCell(settingDummy[indexPath.row])
            return cell