스위프트 심화1

이은호·2023년 6월 28일
0

swift

목록 보기
7/8
post-thumbnail

옵셔널

옵셔널은 안전성을 높이기 위해 사용되는 개념이다.(가급적 오류를 발생시키지 않으려는) 즉 nil을 사용할수 있는 타입을 옵셔널타입이라고 부른다.
중요한 점은 오류가 발생할 가능성이다. 가능성이 있다면 무조건 옵셔널타입으로 정의해야한다.
또한 반환된 값은 언래핑을 통해 사용해야한다.

옵셔널타입 선언과 정리

// 옵셔널 Int 타입
var optInt: Int?

// 옵셔널 String 타입
var optStr: String?

// 옵셔널 Double 타입
var optDouble: Double?

// 옵셔널 Array 타입
var optArray: [String]?

// 옵셔널 Dictionary 타입
var optDic: Dictionary<String, String>?
var optDic2: [String: String]?

// 옵셔널 Class 타입
var optClass: AnyObject?

타입뒤에 ?을 붙여주기만 하면 되겠다.

옵셔널 값 처리

옵셔널타입끼리는 사실 쓸모가 없다. 연산을 지원하지도 않는다. +-*/조차도

앞에서 말했듯이 언래핑(옵셔널해제)를 해야한다.
옵셔널해제

  • 명시적 해제
    • 강제적 해제
    • 비강제적 해제
  • 묵시적해제
    • 컴파일러에 의한 자동해제
    • 연산자에 의한 자동해제

옵셔널 강제 해제

강제 하는 방법은 옵셔널 타입뒤에 !만 붙여주면 된다.
단 nil을 반환한다면 오류를 출력한다.
때문에 강제 해제 연산자를 사용한다면 반드시 if조건절을 사용해야한다.

var str = "123"
var intFromStr = Int(str)

if intFromStr != nil {
    print("변환된 값은 \(intFromStr!)입니다")
} else {
    print("값 변환에 실패하였습니다")
}

참고로 Int()는 옵셔널을 반환한다. String()도? 아니 여기선 오류가 일어날 일이 없기때문에 문자열 그대로 반환한다.

비강제적 해제(옵셔널 바인딩)

var str = "Swift"

if let intFromStr = Int(str) {
    print("변환된 값은 \(intFromStr)입니다")
} else {
    print("값 변환에 실패하였습니다")
}

위와 비슷해보이지만, 상수 선언이 되면 옵셔널이 아닌, 일반타입이다. 마찬가지로 nil이면 else구문이 실행된다.
이번엔 guard구문을 써보자

func intStr(str: String) {
    guard let intFromStr = Int(str) else {
        print("값 변환에 실패하였습니다")
        return
        
    }
    print("변환된 값은 \(intFromStr)입니다")
}

딕셔너리 키로 접근하면 값은 옵셔널타입

컴파일러 자동해제

옵셔널타입변수를 사용하려면 !을 사용해야한다고 했는데 그렇디 않는 경우도 있다. 바로 비교연산자를 사용할때다. 그때는 자연스럽게 언래핑이 된다.
let optInt = Int("123")

if (optInt == 123) {
print("optInt == 123")
} else {
print("optInt != 123")
}

묵시적해제

?대신 !을 사용한다. 그래서 일반 변수마냥 쓸수 있다. 주의해야할점은, nil이 될 가능성이 있다면, 이 테크를 쓰지 않는 것이 좋다.

// 명시적 옵셔널
var str: String? = "Swift"
print(str)

// 실행 결과
Optional("Swift")

// 묵시적 옵셔널
var str: String! = "Swift"
print(str)

// 실행 결과
Swift

함수

함수의 정의는 생략하고, 스위프트가 함수형 프로그래밍 패러다임을 채택하고 있는 언어이기 때문에 함수형 프로그래밍의 특성을 이해하는 것은 매우 중요하다.

호출시 매개변수 레이블 작성

파이썬과 달리 파라미터의 레이블을 항상 작성해야한다.

func times(x: Int, y: Int) -> Int {
    return (x * y)
}
times(x: 5, y: 10) 

typalias

typealias infoResult = (Int, Character, String)

func getUserInfo() -> infoResult {
    let gender: Character = "M"
    let height = 100
    let name = "Peter"
    
    return (height, gender, name)
}

타입 알리어스는 이름이 길거나 사용하기 복잡한 타입표현을 새로운 타입명으로 정의해주는 문법

typealias infoResult = (h: Int, g: Character, n: String)

func getUserInfo() -> infoResult {
    let gender: Character = "M"
    let height = 100
    let name = "Peter"
    
    return (height, gender, name)
}

이렇게도 쓸수 있다.

매개변수

함수 선언시 언더바를 넣으면 레이블을 생략할수 있다.

func printHello(_ name: String, _ msg: String) {
    print("\(name)님, \(msg)")
}

printHello("홍길동", "안녕하세요")

가변인자

매개변수가 여러개일경우도 있다.

func avg(score: Int...) -> Double {
    var total = 0 // 점수 합계
    for r in score { // 배열로 입력된 값들을 순회 탐색하면서 점수를 합산
        total += r
    }
    return (Double(total) / Double(score.count)) // 평균 구해서 반환
}

print(avg(score: 10,20,30,40)) // 결과 : 25.0

일급 객체

일급객체 조건
1. 객체가 런타임에도 생성이 가능하다
2. 인자값으로 객체를 전달할 수 있다.
3. 반환값으로 객체를 사용할 수 있다.
4. 변수나 데이터 구조안에 저장할 수 있다.
5. 할당에 사용된 이름에 관계없이 고유한 구별이 가능해야한다.

위 조건을 만족하면 일급함수가 되며, 그 언어를 함수형 언어로 분류한다.

func successThrough() {
    print("연산 처리가 성공했습니다")
}

func failThrough() {
    print("처리 과정에 오류가 발생했습니다")
}

func divide(base: Int, success sCallBack: () -> Void, fail fCallBack: () -> Void) -> Int {
    guard base != 0 else {
        fCallBack() // 실패 함수를 실행한다
        return 0
    }
    
    defer {
        sCallBack() // 성공 함수를 실행한다
    }
    return 100 / base
}

divide(base: 30, success: successThrough, fail: failThrough)
// 실행 결과
연산 처리가 성공했습니다

defer블록은 코드에 상관없이 가장 마지막에 실행된다.

클로저

쉽게말해, 람다, 화살표 함수같은거라고 생각하면 되겠다.

트레일링 클로저

트레일링 클로저는 함수의 마지막 인자값(1개)이 클로저일때, 이를 인자값형식으로 작성하는 대신 함수의 뒤에 꼬리처럼 붙이는것.

value.sort() { (s1, s2) in
    reutnr s1 > s2
}

{}안의 코드들은 원래 ()안에 있어야하지만, 스위프트에선 꺼내서 작성할수도 있다.

value.sort { (s1, s2) in
    reutnr s1 > s2
}

만약 파라미터가 한개라면 ()도 생략할수 있다.

@escaping && @autocolsure

클로저를 위와같이 인자값으로 사용할때 부여할수 있는 속성이다.
이스케이핑은 인자값으로 전달된 클로저를 전달해 두었다가 나중에 다른 곳에서도 실행할 수 있도록 허용해주는 속성이다.

func callback(fn: () -> Void) {
    let f = fn // 클로저를 상수 f에 대입 
    f() // 대입된 클로저를 실행
}

위코드의 일반적인클로저는 다른곳에 대입될 수 없다.

func callback(fn: @escaping () -> Void) {
    let f = fn // 클로저를 상수 f에 대입
    f() // 대입된 클로저를 실행
}

속성을 부여해줌으로써 가능해졌다.


오토클로저속성은 인자값으로 전달된 구문과 함수를 클로저로 래핑한다. 예제로 보자.

// 함수 정의
func condition(stmt: () -> Bool) {
    if stmt() == true {
        print("결과가 참입니다")
    } else {
        print("결과가 거짓입니다")
    }
}

그리고 사용시에는 보통 이렇게 사용할 것이다.

// 1. 일반 구문
condition(stmt: {
    4 > 2
})

// 2. 클로저 구문
condition {
    4 > 2
}

그러나 오토클로저속성을 부여하면 인자값을 넣으면 알아서 클로저로 만들어준다. 추가적으로 위 두가지 사용은 할수없다.

func condition(stmt: @autoclosure () -> Bool) {
    if stmt() == true {
        print("결과가 참입니다")
    } else {
        print("결과가 거짓입니다")
    }
}
// 실행 방법
condition(stmt: (4 > 2))

질문

guard는 단독으로 사용이 불가한가? while func안에 있어야만 하는가

0개의 댓글