Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift 기초 #35

Open
simoniful opened this issue Aug 17, 2022 · 0 comments
Open

Swift 기초 #35

simoniful opened this issue Aug 17, 2022 · 0 comments

Comments

@simoniful
Copy link
Owner

simoniful commented Aug 17, 2022

Swift 기본 지원 타입

  • Int, UInt
  • Double, Float
  • Bool
  • String, Character
  • Collection - Array, Dictionary
  • Tuple
  • Optional

상수와 변수

1) 선언

상수 값은 한번 설정된 후로는 변경할 수 없으나, 변수는 이후에도 변경될 수 있음.

  • 상수의 keyword - let
  • 변수의 keyword - var
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

2) 타입 명시

Swift는 기본적으로 타입을 명시하지 않지만 명확하게 하기 위해 타입 명시
선언 시점에서 초기값을 제공하면 유추하여 결정

var welcomeMessage: String
welcomeMessage = "Hello"
var red, green, blue: Double

3) 네이밍

공백 문자, 수학 기호, 화살표, 전용 유니코드 스칼라 값 또는 선 및 상자 그리기 문자를 사용할 수 없다
숫자가 이름의 다른 곳에 포함될 수 있지만 숫자로 시작할 수도 없다
상수나 변수에 예약된 Swift 키워드와 동일한 이름을 지정해야 하는 경우 `(백틱)을 통한 묶음을 피치 못할 경우 사용한다

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"
let `let` = "Jin"

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"

let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.

프린트

print(_:separator:terminator:) 내장 메서드를 사용하여 상수 또는 변수의 현재 값을 인쇄할 수 있다
문자열 보강을 활용하여 placeholder로서 상수와 변수를 활용 가능, (백 슬래시)와 괄호 사용

print(friendlyWelcome)
// Prints "Bonjour!"
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"

주석

다른 언어와 마찬가지로 주석 작성 가능, 네스팅 된 주석이 가능

// This is a comment.
/* This is also a comment
but is written over multiple lines. */
/* This is the start of the first multiline comment.
    /* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */

Int

Int 타입은 정수 범위 모든 범위, UInt 타입은 정수 양수 범위를 가진다

1) Integer Bounds

내부 프로퍼티를 통해서 해당 타입의 최소/최대 값을 활용할 수 있다 - 명시적으로 활용에 있어서 매직 넘버보다 낫다

let minValue = UInt8.min  // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max  // maxValue is equal to 255, and is of type UInt8

2) Int

Swift는 8, 16, 32, 64 bits 형태의 Int 타입을 제공하며, 구체적으로 개발자가 설정할 필요 없이 플랫폼에 맞춰서 swift 내에서 조절한다

3) UInt

양수와 0 기반의 데이터를 제공하며, 특별히 필요한 경우에만 사용(ex, 창고의 재고 데이터), 유추로 인한 불확실성을 제거하고 안정성을 확보

protocol StockManager {
    mutating func reflect(orderCount: [String: Int])
}

public class Market: StockManager {
    var prodStock: [String: UInt] = [:]
    
    init(prodStock: [String: UInt]) {
        self.prodStock = prodStock
    }
    
    func reflect(orderCount: [String : Int])  {
        for (name, count) in orderCount {
            guard let prodCount = prodStock[name] else { return }
            
            if Int(prodCount) - count < 0 {
                self.prodStock[name] = 0
            } else {
                self.prodStock[name] = prodCount - UInt(count)
            }
        }
    }
}

let marketAprodStock: [String: UInt] = [
    "감자": 9,
    "당근": 4,
    "호박": 7
]

let orderCount: [String: Int] = [
    "감자": 10,
    "당근": 3,
    "호박": 7
]

var marketA = Market(prodStock: marketAprodStock)
marketA.reflect(orderCount: orderCount)
marketA.prodStock
// ["호박": 0, "감자": 0, "당근": 1]

부동소수점 수(Double/Float)

Double은 64-bit 부동 소수점, Float은 32-bit 부동 소수점
Double 타입은 소수점 이하 15자리의 정밀도를 가지고, Float 타입은 소수점 이하 6자리의 정밀도를 가진다

타입 세이프와 타입 추정

Swift는 타입 세이프 언어로 값의 타입을 명확하게 표현(ex. String 타입으로 예상되는 부분이라면 Int 값을 전달하는건 불가능)
컴파일 시 일치하지 않으면 에러로 표시하며 개발시 가능한 일찍 오류를 고칠 수 있도록 함 - 런타임이 아니라 다행
타입 추정은 상수나 변수의 초기 값을 같이 선언할 때 유용(초기 값을 설정시 해당 값의 타입으로 가진다고 추정)

let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
// Swift는 타입 추정 시 Float보다 Double을 선택

숫자의 문자 표현(Numeric Literals)

  • 10진수 - 접두사 없음
  • 2진수 - 접두사 0b
  • 8진수 - 접두사 0o
  • 16진수 - 접두사 0x
let decimalInteger = 17
let binaryInteger = 0b10001       // 17 - 2진수
let octalInteger = 0o21           // 17 - 8진수
let hexadecimalInteger = 0x11     // 17 - 16진수

부동 소수점의 Numeric Literals은 10진수 또는 16진수일 수 있다
소수점을 기점으로 양쪽에 항상 10진수 또는 16진수 숫자가 있어야 한다
10진수 부동 소수점는 대/소문자로 표시되는 선택적 지수 e를 가질 수도 있다
16진수 부동 소수점은 대/소문자로 표시되는 지수 p가 반드시 있어야 한다

  • 10진수
    • 1.25e2 means 1.25 x 10^2, or 125.0.
    • 1.25e-2 means 1.25 x 10^(-2), or 0.0125.
  • 16진수
    • 0xFp2 means 15 x 2^2, or 60.0.
    • 0xFp-2 means 15 x 2^(-2), or 3.75.

이러한 모든 부동 소수점 Numeric Literals의 10진수 값인 12.1875은 다음처럼 표현 가능하다.

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

Numeric Literals는 읽기 쉽도록 추가 서식이 포함될 수 있다. 정수와 부동 소수점은 모두 0으로 채워질 수 있으며 가독성을 돕기 위해 밑줄을 포함할 수 있다. 모두 리터럴의 기본 값에 영향을 주지 않는다

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

숫자 유형의 타입 전환(Numeric Type Conversion)

Int 타입 자체의 상호유형 변환이 자유롭기에 일반적으로 사용한다
다른 Int 유형들은 특정한 사이즈의 데이터를 다루는 작업, 성능, 메모리 사용 또는 기타 필요한 최적화등 특별히 필요한 경우에만 사용한다

1) 정수 변환

다른 유형의 Int를 사용할 때 호환과 제한에 주의

let cannotBeNegative: UInt8 = -1
// UInt8 음수를 다룰 수 없기에 에러 출력
let tooBig: Int8 = Int8.max + 1
// Int8 에서 max 사이즈보다 큰 수 핸들링 불가에러 출력
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
// 상호 호환이 되지 않기에 변환하여 연산자 수행

2) 정수와 부동소수점 간 변환

정수와 부동 소수점 숫자 유형 간의 변환은 명시적으로 구체화
Int는 Double또는 Float값으로 초기화를 통해 연산에 사용
Double 또는 Float는 Int 변환 시 정수 값만 도출

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int

Typealias

타입의 이름을 대체하여 다른 이름으로 정의

typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

Boolean

true, false 두 가지 상수 값만 가짐
조건문 구성에 유용
다른 언어와 같이 0 또는 1로 대체 불가 - 타입 안정성으로 인한 컴파일 에러

let orangesAreOrange = true
let turnipsAreDelicious = false

Tuple

간단하게 여러 값을 단일 복합 값으로 그룹화, 내부 타입은 어떤 형식도 가능
튜플의 내부 값을 분리하여 상수와 변수에 저장 가능, 필요한 원소가 아닌 경우 _(와일드 카드)를 통하여 무시 가능
튜플 자체의 인덱스를 통하여 접근 가능
개별 항목에 이름 붙이고 이를 기반으로 접근 가능
함수의 반환 값으로 사용하기에 유용(ex. 네트워크 통신 - 통신의 성공, 통신으로 받은 JSON)

let http404Error = (404, "Not Found")

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"

print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"

let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"

Optional

값이 없을 수 있는 nil이 가능한 상황에서의 타입 정의
따라서, 값이 있고 그에 접근하기 위한 언래핑 과정(옵셔널 바인딩) / 값이 없는 nil 상황을 위한 구성

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber는 변환이 되지 않을 수 있기에 "Int?", 또는 "optional Int"으로 타입 추론

1) nil

옵셔널로 정의한 변수는 nil을 할당하는 것이 가능
항상 nil은 옵셔널과 관계있는 경우에 사용 가능
default 값을 할당하지 않고 변수를 선언할 경우 자동으로 nil 할당

var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
var surveyAnswer: String?
// surveyAnswer is automatically set to nil

2) If 문과 Forced Unwrapping

if 조건문에서 옵셔널이 값을 가지고 있는지 판단하기 위해 nil과 비교할 수 있음 - 옵셔널이 값을 가지고 있다면 nil이 아님
옵셔널이 반드시 값을 가지고 있다고 확신하면 느낌표(!)를 옵셔널 이름 끝에 추가하여 강제 언래핑이 가능
! 강제 언래핑 사용 시 옵셔널에 값이 없으면 Error가 발생

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."

var cabBeNil: Int?
println(cabBeNil)
// Compile is OK
println(cabBeNil!)
// Error, canBeNil is no value

3) 옵셔널 바인딩(Optional Binding)

if let constantName = someOptional {
    statements
}

위와 같은 형식의 할당을 통하여 해당 값을 단일 작업의 일부로 상수 또는 변수로 추출
이 외에도 guard let 구문을 통한 바인딩도 구성 가능

let myNumber = Int(possibleNumber)
if let myNumber = myNumber {
    print("My number is \(myNumber)")
}
// Prints "My number is 123"
// 같은 네이밍으로 할당하면서 optional Int 값에서 Int 값 바인딩 가능
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// Prints "4 < 42 < 100"
// 단일적인 해제 뿐만 아니라 조건의 추가를 통하여 복합적인 구성 가능

암시적으로 언래핑한 옵셔널(implicitly unwrapped optionals)

옵셔널에 값이 무조건 들어있다는 것을 명확하게 알 수 있을 때, 간결한 코드를 위해서 쓰는 방식
일반적인 옵셔널과는 다르게 언래핑을 하지 않아도 일반적인 변수나 상수로 쓸 수 있다

let one : Int? = 1 
// 일반적인 옵셔널
// one는 옵셔널 이긴하지만 nil이 아닌 값이 무조건 들어있는 상황
let other : Int! = 1 
// 암시적으로 언래핑한 옵셔널
print(one)
print(other)

암시적으로 벗겨진 옵셔널 a를 선언
a는 언래핑을 거치지 않고 Int형인 b에 값을 할당 가능
a에 언래핑을 통해서도 Int형인 c에 값을 할당 가능 - 여기서는 force umwrapping
a를 다른 변수인 d에 할당하면 마찬가지로 암시적으로 벗겨진 옵셔널 타입으로 할당
a는 언래핑을 거치지 않고 바로 값에 접근할 수 있으므로 a+1과 같은 연산을 언래핑을 하지 않고도 수행 가능

let a : Int! = 1
let b : Int = a  
let c : Int = a!
let d = a        
let e = a + 1

print(a,b,c,d,e)
print(type(of: a))
print(type(of: b))
print(type(of: c))
print(type(of: d))
print(type(of: e))

암시적으로 벗겨진 옵셔널도 옵셔널이기 때문에 nil 값을 할당 후에 강제 언래핑할 경우 오류가 난다
암시적으로 벗겨진 옵셔널 a를 선언 후, a에 nil을 할당하고 a와 a!를 출력

var a : Int! = 1 
a = nil

print(a)
print(a!)
// nil
// error 발생

에러 핸들링

값의 유무를 사용하여 함수의 성공 또는 실패를 전달할 수 있는 Optional과 달리
에러 핸들링을 사용하면 실패의 근본 원인을 확인하고 필요한 경우 프로그램의 다른 부분에 오류를 전파할 수 있다.

func canThrowAnError() throws {
    // 에러의 가능성에 대비한 함수 구성
}

do {
    try canThrowAnError()
    // 에러 없이 로직이 정상 수행
} catch {
    // 에러가 발생할 경우 로직 
}

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}
// 에러의 구체화를 통한 에러 핸들링 가능

Assertions and Preconditions

runtime 디버깅 환경에서 추가 코드를 실행하기 전에 essential condition을 만족시켰는 지 검증하는 데 사용
코딩하는 동안 만든 가정과 기대치를 확인하는 것이 중점
조건이 true 면 코드는 계속 실행, false 면 현재 프로그램 상태가 invalid 하다고 판단되어서 코드 실행이 멈추고 설정한 메시지 표시 후 앱이 종료

Assertion은 개발 중에 실수와 잘못된 가정을 찾는 데 도움이 되며 Preconditions은 프로덕션에서 문제를 감지하는 데 도움
에러 핸들링과는 다르게 분기처리를 통한 복구가 목적이 아니며, 프로그램의 종료로 그 해당 원인을 파악하기 용이
invalid state 에서 실행을 중지하는 것은 잘못된 상태로 인해 발생할 수 있는 damage를 제한

1) Debugging with Assertions

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 isn't >= 0.

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}
// 만약 코드 내에서 invalid state가 체킹되었다면 assertionFailure(_:file:line:) 메서드를 통해 메시지만 출력하고 종료되도록 유도 가능

2) Enforcing Preconditions

조건(condition)이 false일 가능성이 있을 때마다 precondition을 사용할 수 있고 다음 코드를 실행하려면 조건은 반드시 true
subscript가 범위를 벗어나지 않았는 지, function이 valid value를 전달 받았는 지 확인할 때 사용 가능

precondition(::file:line:)에 조건과 조건의 결과가 false 일 경우 표시할 메세지를 작성

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")

preconditionFailure(_:file:line:) function 을 호출해서 failure이 발생한 상황을 print 가능

Assertion은 디버그 빌드에서만 확인되지만 Preconditions은 디버그 및 프로덕션 빌드 모두에서 확인 가능
즉, 프로덕션 빌드에서 assertion 안에 있는 condition은 평가되지 않음 - 빌드 세팅에 따라 다르니 유의할 것
이는 프로덕션 성능에 영향을 주지 않으면서 개발 과정 중, 많은 assertions 을 사용할 수 있다는 것을 의미

3) FatalError

fatalError(_:file:line:) function은 위와 같은 optimization settings 에 상관없이 항상 실행을 중단
프로토 타입 또는 초기 개발 중에 fatalError를 사용해서 아직 구현되지 않은 기능에 대한 stub을 생성 가능
ex. stub 구현으로 fatalError("Unimplemented")를 작성

참고

👉🏻 애플 공식 문서
👉🏻 민소네 블로그
👉🏻 은딩 블로그

@simoniful simoniful changed the title 기본 연산자 Swift 기초 Oct 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant