IOS/Swift

Swift 기본 문법 정리

montt 2022. 9. 28. 20:34

Swift 기본 문법

상수와 변수 (let, var)


  1. 상수 : 데이터가 한번 초기화되면 더이상 변하지 않는 수
  2. 변수 : 데이터를 계속해서 변경할 수 있는 수
let a: Int = 1 // 상수
var b: Int = 2 // 변수

a = 2 // 오류 발생

데이터 타입(Int, UInt, Float, Double, Bool, Character, String, Any)


  • Int : 64bit 정수형
  • UInt : 부호가 없는 64bit 정수형
  • Float: 32bit 부동 소수점
  • Double: 64bit 부동 소수점
  • Bool: true / false
  • Character: 문자형
  • String: 문자열
  • Any: 모든 타입을 지칭하는키워드

컬렉션 타입(Array, Dictionary, Set)


  • Array : 데이터 타입의 값들을 순서대로 저장하는 리스트
  • Dictionary : 순서없이 키(key)와 값(Value) 한 쌍으로 데이터를 저장하는 컬렉션 타입
  • Set : 같은 데이터 타입의 값을 순서없지 저장하는 리스트
// Array
var numbers: Array<Int> = Array<Int>()
// 추가
numbers.append(1)
numbers.append(2)
numbers.append(3)

// 중간지점 추가
numbers.insert(4, at: 2)

// 해당 인덱스 삭제
numbers.remove(at: 0)

// Dictionary 초기화 선언
var dicData: Dictionary<String, Int> = Dictionary<String, Int>()
// 초기값 선언
var dic: [String: Int] = ["홍길동": 1]
dic["김철수"] = 3
dic["김민지"] = 5
dic["김민지"] = 6
//삭제
dic.removeValue(forKey: "김민지")

// Set 초기화
var set: Set = Set<Int>()

// set 추가
set.insert(10)
set.insert(20)
set.insert(30)
// 중복 추가 안됌
set.insert(30)

// set 삭제
set.remove(20)

함수 정의(func)


/*
    func 함수명(파라미터 이름: 데이터 타입) -> 반환 타입 {
            return 반환 값
    }
*/

// 더하기 함수
func sum(a: Int, b: Int) -> Int {
    return a+b
}

// hello 반환하는 함수
func hello() -> String {
    return "hello"
}

// me의 기본값으로 Gildong 가지는 함수
func greeting(friend: String, me: String = "Gildong") {
    print("Hello, \(friend)! i'm \(me)")
}
// 함수 호출 : "Hello, Minji! i'm Gildong"
greeting(friend: "Minji")

// 매개변수를 사용하는 함수
func sendMessage(from myName: String, to name: String) -> String {
    return "Hello \(name)! I'm \(myName)"
}
// 함수 호출 : "Hello Minji! I'm Gildong"
sendMessage(from: "Gildong", to: "Minji")

// 전달인자를 사용하지 않는 함수
func sendMessage(_ name: String) -> String {
    return "Hello \(name)"
}

조건문(if, switch)


/* 
    if 조건식 {
        실행할 구문
    }
*/
let age = 12
if age < 19 { 
    print("미성년자 입니다.")
}

/*
    if 조건식1 {
        조건식1을 만족하면 해당 구문 실행
    } else if 조건식 2 {
        조건식2를 만족하면 해당 구문 실행
    } else {
        아무 조건식도 만족하지 않을 때 실행할 구문
    }
*/

let animal = "pig"
if animal == "dog" {
    print("개")
} else if animal == "cat" {
    print("고양이")
} else {
    print("기타 동물")
}

/*
    switch 비교대상 {
        case 패턴1:
            // 패턴1 일치할 때 실행되는 구문
        case 패턴2, 패턴3:
            // 패턴2, 패턴3이 일치할 때 실행되는 구문
        default:
            // 어느 비교 패턴과도 일치하지 않을 때 실행되는 구문
    }
*/

let color = "green"
switch color {
case "blue":
    print("파란색 입니다.")
case "green":
    print("초록색 입니다.")
case "pink":
    print("분홍색 입니다.")
default:
    print("찾는 색상이 없습니다.")
}

let temperature = 30

switch temperature {
case -20...9: //-20에서 9 사이
    print("겨울 입니다.")
case 10...14:
    print("가을 입니다.")
case 15...25:
    print("봄 입니다.")
case 26...35:
    print("여름 입니다.")
default:
    print("이상 기후 입니다.")
}

반복문(for in, while, repeat while)


반복적으로 코드가 실행되게 만드는 구문을 말한다.

/*
    for 루프상수 in 순회대상 {
        // 실행 구문
}
*/
for i in 1..4 {
    print(i) // 1 2 3 4
}

// 컬렉션 타입을 순회대상으로
let array = [1,2,3,4,5]
for i in array {
    print(i) // 1 2 3 4 5
}

/*
    while 조건식 {
        // 실행 구문
    }    
*/
var number = 5
while number < 10 {
    number +=1
}

/* 다른 언어의 do~while 구문과 동일
    repeat {
        // 실행할 구문
    } while 조건식
*/
var x = 6
repeat {
    x+=2
} while < 5

옵셔널 (nil)


  1. 값이 있을 수도 있고, 없을 수도 있는 값
  2. var name: String? // Optional nil var optionalName: String? = "Gildong" // Gildong print(optionalName) // Optional("Gildong")\n

옵셔널 바인딩


  1. 명시적 해제 : 강제 해제, 비강제 해제(옵셔널 바인딩)
  2. 묵시적 해제 : 컴파일러에 의한 자동 해제, 옵셔널의 묵시적 해제
// 명시적 해제
// 값이 nil인 optional 강제로 해제하면 에러가 발생한다.
var number: Int? = 3
print(number) // Optional(3)\n
print(number) // 3\n

// Optional 해제 시 에러가 발생하면 else문 실행
if let result = number {
    print(result)
} else {
}

// guard문에서 사용하면 계속해서 상수를 사용할 수 있다.
func test() {
    let number: Int? = 5
    guard let result = number else { return }
    print(result)
}

// 묵시적 해제
// 비교 연산자로 비교
let value: Int? = 6
if value == 6 {
    print("value가 6입니다.")
} else {
    print("value가 6이 아닙니다.")
}

// string의 값이 숫자가 아닐경우 Int로 파싱하면 nil이 된다.
// 따라서 Optional 지정을 해야한다.
// Int! 느낌표는 묵시적으로 옵셔널을 해제한다.
let string = "12"
var stringtoInt: Int! = Int(string)

구조체(struct)


/*
        struct 구조체 이름 {
            프로퍼티와 메소드
        }
*/

// 구조체와 클래스의 이름은 파스칼 정의법(대문자 시작)을 따른다.
struct User {
    var nickname: String
    var age: Int

    func information() {
        print("\(nickname) \(age)")
    }
}

var user = User(nickname: "Gildong", age: 30)
user.nickname
user.age

// Gildong 30
user.information()

클래스(class)

/*
        class 클래스 이름 {
            프로퍼티와 메서드
        }
*/

class Dog {
    var name: String = ""
    var age: Int = 0

    func introduce() {
        print("name \(name) age \(age)")
    }
}

var dog = Dog()
dog.name = "CoCo"
dog.age = 3
// name CoCo age 3
dog.introduce()

초기화구문 init


클래스 구조체 또는 열거형의 인스턴스를 사용하기 위한 준비 과정

/*
    init(매개변수: 타입, ...) {
        // 프로퍼티 초기화
        // 인스턴스 생성시 필요한 설정을 해주는 코드 작성
}
*/

class User {
    var nickname: String
    var age: Int

    init(nickname: String, age: Int) {
        self.nickname = nickname
        self.age = age
    }

    init(age: Int) {
        self.nickname = "grace"
        self.age = age
    }

    deinit {
        print("deinit user")
    }
}

var user = User(nickname: "Gildong", age: 30)
user.nickname // "Gildong"
user.age // "30"

var user2 = User(age: 27)
user2.nickname //grace
user2.age // 27

user2 = nil // deinit 호출

 

프로퍼티


클래스, 구조체 또는 열거형 등에 관련된 값을 뜻한다.

  1. 저장 프로퍼티
  2. 연산 프로퍼티
  3. 타입 프로퍼티

클래스와 구조체 차이


공통점

  • 값을 적어할 프로퍼티를 선언할 수 있다.
  • 함수적 기능을 하는 메서드를 선언할 수 있다.
  • 내부 값에 . 을 사용하여 접근할 수 있다.
  • 생성자를 사용해 초기 상태를 설정할 수 있다.
  • extension을 사용하여 기능을 확장할 수 있다.
  • Protocol을 채택하여 기능을 설정할 수 있다.

차이점

클래스

  • 참조 타입
  • ARC로 메모리를 관리
  • 상속이 가능
  • 타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입을 확인할 수 있음
  • deinit을 사용하여 클래스 인스턴스의 메모리 할당을 해제할 수 있음
  • 같은 클래스 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경 시키면 모든 변수에 영향을 줌

구조체

  • 값 타입
  • 구조체 변수를 새로운 변수에 할당할 때마다 새로운 구조체가 할당된다.
  • 즉 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시키더라도 다른 변수에 영향을 주지 않는다.

상속


부모가 자식에게 재산을 물려주는 행위

/*
class 클래스 이름: 부모클래스 이름 {
    // 하위 클래스 정의
}
*/
class Vehicle {
    var currentSpeed = 0
    func makeNoise() {
    }
}

class Bicycle: Vehicle {
    var hasBasket = false
}

var bicycle = Bicycle()
bicycle.currentSpeed // 0

// override 부모 프로퍼티를 재정의
// super.makeNoise() 부모 프로퍼티 상속
// final class : override 사용 불가능
class Train: Vehicle {
    override func makeNoise() {
        print("choo choo")
    }
}

타입캐스팅(is, as)


인스턴스의 타입을 확인하거나 어떠한 클래스의 인스턴스를 해당 클래스 계층 구조의 슈퍼 클래스나 서브 클래스로 취급 하는 방법

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

class Song: MediaItem {
    var artist: String
    init(name:String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}

let library = [
    Movie(name: ..., director: ...),
    Song(name: ..., artist: ...),
    ...
]

// is연산자 item 의 클래스가 Movie일 경우 해당 프로퍼티 실행
for item in library {
    if item is Movie {
        print("movie")
    }
}

// as연산자 : 다운캐스팅
// as? -> 조건부 형식 다운캐스팅
// as! -> 강제 다운캐스팅 : 잘못된 타입으로 다운캐스팅 시 런타임 에러 발생
if let movie = item as? Movie

assert와 guard


  1. assert
    • 특정 조건을 체크하고, 조건이 성립되지 않으면 메세지를 출력 하게 할 수 있는 함수
    • assert 함수는 디버깅 모드에서만 동작하고 주로 디버깅 중 조건의 검증을 위하여 사용한다.
  2. gurad
    • 뭔가를 검사하여 그 다음에 오는 코드를 실행할지 말지 결정하는 것
    • guard 문제 주어진 조건문이 거짓일 떼 구문이 실행됨
// assert
var value = 0
assert(value == 0)
value = 2
// Error 발생
assert(value == 0, "값이 0이 아닙니다.)

// guard
/*
    guard 조건 else {
        // 조건이 false 이면 else 구문이 실행되고
        return opr throw or break 를 통해 이 후 코드를 실행하지 않도록 한다.
    }
*/
func guardTest(value: Int) {
    guard value == 0 else { return }
    print("안녕하세요")
}

guardTest(value: 3)
guardTest(value: 0) // 안녕하세요

// guard 문을 통한 Optional 바인딩
func guardTest2(value: Int?){
    guard let value = value else { return }
    print(value)
}

guardTest2(value: 2) // 2

프로토콜


특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진

protocol SomeProtocol {
    var name: Int { get set } // 읽고 쓰기 가능
    var age: Int { get } // 읽기만 가능
    func printPerson()
}

// 상속
struct SomeStructure: SomeProtocol {
    var name: Int
    var age: Int
    func printPerson() {
        print(name)
    }
}

익스텐션


기존의 클래스, 구조체, 열거형, 프로토콜에 새로운 기능을 추가하는 기능

  • 익스텐션이 타입에 추가할 수 있는기능
    • 연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티
    • 타입 메서드 / 인스턴스 메서드
    • 이니셜라이저
    • 서브스크립트
    • 중첩 타입
    • 특정 프로토콜을 준수할 수 있도록 기능 추가
extension Int {
    var isEven: Bool {
        return self % 2 == 0
    }
    var idOdd: Bool {
        return self % 2 == 1
    }
}

var number = 3
number.isOdd // true
number.isEven // false

열거형 (enum)


연관성이 있는 값을 모아 놓은 것을 말한다. 각 항목은 그 자체로 고유값이다.

enum CompassPoint {
    case north
    case south
    case east
    case west
}

var direction = Compasspoint.east // east

enum PhoneError {
    case unknown
    case batteryLow(String)
}

let error = PhoneError.batteryLow("배터리가 곧 방전됩니다") // 연관값 넘겨받기

옵셔널 체이닝


옵셔널에 속해 있는 nil 일지도 모르는 프로퍼티, 메서드, 서브스크립션 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정

struct Developer {
    let name: String
}

struct Company {
    let name: String
    var developer: Developer?
}

var developer = Developer(name: "Hong")
var company = Company(name: "Gildong", developer: developer)
print(company.developer)
print(company.developer?.name)
print(company.developer!.name)

try-catch


에러처리 : 프로그램 내에서 에러가 발생한 상황에 대응하고 이를 복구하는 과정을 말하며 스위프트는 런타임에러가 발생한 경우 이를 처리하기 위해 발생, 감지, 전파, 조작을 관리하는 클래스를 제공한다.

enum PhoneError: Error {
    case unknown
    case batteryLow(batteryLevel: Int)
}

// 에러 발생
throw PhoneError.batteryLow(batteryLevel: 20)

// 쓰로잉 함수
func checkPhoneBatteryStatus(batteryLevel: Int) throws -> String {
    guard batteryLevel != else { throw PhoneError.unknown }
    guard batteryLevel >= 20 else { throw PhoneError.batteryLow(batteryLevel: 20)}
    return "배터리 상태가 정상입니다."
}

// do try catch
/*
do {
    try 오류 발생 가능 코드
} catch 오류 패턴 {
    처리코드
}
*/
do {
    try checkPhoneBatteryStatus(batteryLevel: -1)
} catch PhoneError.unknown {
    print("알 수 없는 에러입니다.")
} catch PhoneError.batteryLow(let batterryLebel) {
    print("배터리 전원 부족 남은 배터리 : \(batterryLebel)%")
} catch {
    print("그 외 오류 발생 : \(error)")
}

let status = try? checkPhoneBatteryStatus(batteryLevel: -1) // nil
let status = try! checkPhoneBatteryStatus(batteryLevel: 30) // 오류 발생시 런타임에러

클로저


코드에서 전달 및 사용할 수 있는 독립 기능 블록이며, 일급 객체의 역할을 할 수 있음

일급객체 : 전달 인자로 보낼 수 있고, 변수/상수 등으로 저장하거나 전달할 수 있으며, 함수의 반환 값이 될 수도 있다.

/*
{ (매개 변수) -> 리턴 타입 in
    실행 구문
}
*/

let hello = { (name:String) -> String in
    return "Hello \(name)"
}

hello("Gildong") // Hello Gildong

고차함수


다른 함수를 전달 인자로 받거나 함수 실행의 결과를 함수로 반환하는 함수

map, filter, reduce

let numbers = [0,1,2,3]
let mapArray = numbers.map { (number) -> Int in
    return number * 2
}
print("map \(mapArray)")      //map [0,2,4,6]

//filter
let intArray = [10,5,20,13,4]
let filterArray = intArray.filter { $0 > 5 }
print("filter \(filterArray)")  // filter [10 ,20, 13]

// reduce
let someArray = [1,2,3,4,5]
let reduceResult = someArray.reduce(0) {
    (result: Int, element: Int) -> Int in
    print("\(result) + \(element)")
    return result + element
}

print("reduce \(reduceResult)")