[TIL]04.23

rbw·2022년 4월 23일
0

TIL

목록 보기
8/98
post-thumbnail

WildCard Pattern

값을 해제하거나 무시하는 패턴 중 하나입니다. 매개변수에서 많이 사용하는 언더스코어(_)는 이 자리에 무엇이 오든 상관하지마라는 뜻입니다. for문이나 switch구문에 많이 사용합니다.

for-in 반복문

swift에서 반복문으로 주로 활용되는 for-in문에 대해서 알아보겠습니다.

문법은 다음과 같습니다. for loopCosntatnt in range { Statement } 반복 상수에 와일드 카드 패턴을 주로 활용하는 경우도 많습니다.

짝수만 출력하는 for-in 반복문

// stride를 사용하는 모습
for num in stride(from: 0, to: 10, by: 2) {
    print(num)
 }

Collection을 사용하는 모습

for-in 문법의 range 위치에 Collection을 넣는 차이가 있다.

let list = [1,2,3]

for num in list {
    print(num)
}

repeat-while 반복문

문법은 다음과 같습니다. repeat { statements } while condition 먼저 반복할 코드가 오는게 일반 while문과 차이가 있다.

var num = 10

while num < 10 {
    num += 1 
}  // num == 10

repeat {
    num += 1
} while num < 10 // num == 11 , 먼저 코드를 실행하기 때문에 이런 차이가 존재함

Labeled Statement

문장의 이름을 붙이는거라고 생각하면 쉽다. 문법은 다음과 같다 Label: statement

// for-in 반복문을 outer라는 이름을 붙여줌. 내부 반복문에서 종료하는 모습.
outer: for i in 1...3 {
    print("Loop", i)
    for j in 1...3 {
        print ("loop2", j)
        break outer
    }
}

Optionals

기존 자료형의 뒤에 ? 를 붙이면 옵셔널 타입이 된다.

let str: String = "HI"
let optionalStr: String? = nil

let num: Int = 3
let optionalNum: Int? = nil

Unwrapping

옵셔널 값을 풀어주는것, 값을 추출한다고 생각하면 된다.

Forced Unwrapping

옵셔널 표현식 뒤에 ! 를 붙여주면 언래핑된다. 강제 언래핑이라고 부름

let num: Int? = 123
print(num) // Optional(123)

print(num!) // 123

nil인 값을 언래핑 하면 에러가 뜨기 때문에, 강제 언래핑은 위험할 수 있다.

Optional Binding

if let, while let, guard let 으로 안전하게 언래핑하는 방법을 알아보겠습니다.

// 옵셔널 표현식을 평가하여 리턴이 된다면 언래핑하여 바인딩한다. 그 후 다음 코드가 실행된다.
if let name: Type = OptionalExpression {
    statements
}
while let name: Type = OptionalExpression {
    statements
}
// guard문은 else문 다음 블록이 실행되는게 성공적인 바인딩이다.
guard let name: Type = OptionalExpression else {
    statements
}

// 조건식과 같이 쓰는 코드
let a: Int? = 12
let b: String? = "hi~"

// 아래 조건이 전부 true이면 내부 실행문이 실행된다.
if let a = a, let str = b, str.count > 2{
    print("okay")
}

let name: Type = OptionalExpression 하는 부분을 바인딩이라고 한다.

암시적으로 추출 옵셔널

특정 컨텍스트에서 자동으로 추출되는 옵셔널, 타입뒤의 ? 가 아닌 ! 를 붙이는 특징이있다.

let num: Int! = 123

let a = num // a는 아직 Int? 타입
let b: Int = num // non-optional타입을 처리할때 언래핑된다. 따라서 b는 Int 타입

nil이 저장되어 있다면 에러가 일어난다.

Nil-Coalescing Opreator

옵셔널에 값이 저장되어 있지 않을때 쉽게 판단하는 연산자.

문법은 다음과 같습니다. a ?? b, OptionalExpression ?? Expression

let name: String? = "minsu"
var msg: String = "hello, "

msg += (name ?? "who?")
print(msg) // "hello, minsu"

옵셔널 값이 리턴하는지 판단을 하고, 값은 언래핑하고 해당 값을 리턴한다. 아니라면 오른쪽 항을 리턴

Optional Chaining

옵셔널 체이닝에서 타입을 결정하는 요소는 마지막 요소로 결정된다. 마지막 요소가 문자열이라면 String? 으로 옵셔널 형식으로 리턴된다.

도중에 옵셔널 표현식에서 nil이 평가되면 그 시점에서 종료된다. 하지만 리턴형은 마지막 요소에서 판단이 되는건 변함이 없다. ㄷ

옵셔널 표현식에서 속성에 접근하거나 함수를 호출할때는 표현식 뒤에 ? 를 붙여줘야 안전하게 접근이 가능하다

마지막 평가하는 요소가 옵셔널이라면 ? 를 붙여주지 않아도 된다. 하지만, 뒤에서 함수를 호출하거나 또 다른 속성에 접근을 하는 경우라면 ? 를 붙여줘야함

// address는 옵셔널
var personA = Person?(name: "minsu", email = "minsu@gamail.com")

// String? 형태, 마지막 address는 ? 생략 가능
let a = personA?.address 

// count를 호출하므로 ?가 필요함
let b = personA?.address?.count 

// 메소드가 옵셔널을 리턴하고 이를 통해 속성에 접근할 경우, 괄호 뒤에 ? 를 붙여줘야한다
personA?.getAddress()?.address 

let p: (() -> Contacts?)? = personA?.getAddress
p?()?.address // 이렇게 함수 자체가 옵셔널이면 괄호 앞에 붙여주는 경우도 있다.

// 옵셔널 void를 리턴하는 메소드 , 실제로 메소드가 호출되는지 확인하는 코드.
if let _ = personA?.getAddress()?.printAddress() {
    ...
}

Optional Pattern

옵셔널 패턴을 활용하는 방법에대해 알아보겠습니다.

// 같은 방식들 이지만 위의 줄이 밑의 줄의 축약 방식이라고 보면 된다.
let a: Int? = 0
let b: Optional<Int> = 0

if a == nil {}
if a == .none {}

if a == 0 {}
if a == .some(0) {} 

if let x = a {
    print(x)
}
if case .some(let x) = a {
    print(x)
}

// 옵셔널 패턴 (enumeration 케이스 패턴의 옵셔널 버전이라고 보면 된다)
if case let x? = a {
    print(x)
}

let list: [Int?] = [0, nil, nil, 3, nil, 5]

for case let x? in list {
    print(x)
} // 0, 3, 5 만 출력, 반복 코드는 3번만 실행된다

Variadic Parameters (가변 파라미터)

하나의 파라미터에 두 개 이상의 값을 전달하는 파라미터

// 파라미터 타입 뒤에 점 세개를 붙이면 적용이 된다. 배열로 받음
func variadicFunc(name: String...){
    ...
 }

가변 파라미터는 하나의 함수에 하나만 적용된다는 점을 주의합시다. 그리고 기본값을 설정할 수 없습니다.

Function Types

문법은 다음과 같다. (ParameterTypes) -> ReturnType

func printHello(with name: String) {
    print("hello, \(name)")
}

// 함수표기법에서 인자라벨만 표기하기 때문에 다음과 같이 작성함
// 실제로 인자를 전달하면 실행하게 된다.
let f1: (String) -> () = printHello(with:)

// 호출시에는 인자 라벨이 필요없다
f1("minsu") // hello, minsu

func add(a: Int, b: Int) -> Int {
    return a + b
}

// 상수에 할당할 때 파라미터 표기를 하지않고, add 만 적어도 작동은 하는듯
var f2: (Int, Int) -> Int = add(a:b:)

typealias를 사용한 리턴타입 설정

함수를 사용하다보면 리턴타입 화살표가 많은걸 볼 수 있는데, 그런 부분에서 가독성을 높이기 위해 typealias를 사용하는 코드

화살표가 많은 경우, 첫번째 화살표가 리턴을 나타내는 화살표이고, 나머지는 형식에 관한 환살표라고 생각을하면 알아보기 쉽다 !

// 사칙연산을 정해서 리턴하는 함수를 만든다고 가정
func divide(_ a: Int, _ b: Int) -> Int {
    return a / b
}

// 이렇게 많은 화살표가 있는 이유는, 먼저 파라미터를 String으로 받고,
// 해당 파라미터에 맞게 스위치문으로 함수를 리턴하기 때문이다.
func selectFunction(from op: String) -> (Int, Int) -> Int? {
    switch op {
        case "+" :
            return add(_:_:)
        case "/" :
            return divide(_:_:)
        default :
            return nil
    }
}
// typealias 사용시
typealias ArithmeticFunction = (Int, Int) -> Int
func selectFunction?(from op: String) -> ArithmeticFunction {
     switch op {
        case "+" :
            return add(_:_:)
        case "/" :
            return divide(_:_:)
        default :
            return nil
    }
}

Nested Functions

함수 내부에 함수를 구현하는 방법을 알아보겠습니다.

func outer() -> () -> () {
    func inner() {
        print("inner")
    }
    print("hi")
    return inner
}

// inner를 호출할 수 있도록 스코프를 확장시킨것.
let ff = outer()  // "hi" 출력
ff() // "inner" 출력

// 이런 방식도 가능

let ff = outer
ff() // "hi" 출력
ff()() // "inner" 출력

함수 단일 리터문으로 구현되어 있다면

return 문을 생략이 가능하다.

func add(_ a: Int, _ b: Int) -> Int {
    a + b // return a + b와 같은 동작
}
func minus(_ a: Int, _ b: Int) -> Int {
    a - b // return a - b와 같은 동작
}

Nonreturning Function

이는 함수를 호출하는 코드 다음으로 제어를 던지지 않는 함수를 뜻한다, 결과는 프로그램을 종료하거나, 에러를 던지는 두 가지 방법만 존재한다.

// Never로 리턴타입을 명시해줘야한다.
// 프로그램을 종료하는 경우
func doSomething() -> Never {
    fatalError("msg")
}

// 에러를 던지는 경우
enum MyError: Error{
    case error
}
// 논리터닝 함수는 항상 에러를 던져야 한다. 에러를 던지는 경우에서는,
func doSomethingThrow() throws -> Never {
    throw MyError.error
}
do {
    try doSomethingThrow()
    print("hi~") // 항상 에러를 던지므로 실행되지 않음
} catch {
    print("error")
}

// guard문과 같이 쓰는 경우
func terminate() -> Never {
    fatalError("bye")
}
// guard문의 else는 코드 실행을 중지해야하므로, Never 타입의 함수를 호출 가능
func doSomething(value: Int) -> Int {
    guard value >= 0 else {
        terminate()
    }

    return 0
}

profile
hi there 👋

0개의 댓글