Swift 기초 문법 정리

Updated:

1.상수와 변수

  • 상수는 변하지 않는 일정한 값

  • 변수는 변할 수 있는 값을 갖는것

import Foundation

// 상수
// let 상수명: 데이터 타입 = 값

let a: Int = 100


// 변수
// var 변수명: 데이터 타입 = 값
var b: Int = 200
b = 400

2.Swift 기본 데이터 타입

  • Int: 64bit 정수가

  • UInt: 부호가 없는 64bit 정수형

  • Float: 32bit 부동 소수점

  • Double: 64bit 부동 소수점

  • Bool: true, false 값

  • Character: 문자

  • String: 문자열

  • Any: 모든 타입을 지정하는 키워드

import Foundation

// Int
var someInt: Int = -100
someInt = 100

// UInt
var someUInt: UInt = 200

// Float
var someFloat: Float = 1.1
someFloat = 1
print(someFloat)

// Double
var someDouble: Double = 1.1
someDouble = 1

// Bool
var someBool: Bool = true
someBool = false

// Character
var someCharacter: Character = "가"
someCharacter = "A"
someCharacter = "😄"

// String
var someString: String = "안녕하세요 👉"

// 타입추론
var number = 1

3.컬렉션 타입

  • Array : 데이터 타입의 값들을 순서대로 지정하는 리스트

  • Dictionary : 순서 없이 key 와 value 의 한 쌍으로 데이터를 저장하는 컬렉션 타입

  • Set : 같은 데이터 타입의 값을 순서 없이 저장하는 리스트

Array 예제

import UIKit

// 빈 Array 만들기
var numbers: Array<Int> = Array<Int>()
// 1을 Array 에 추가하기
numbers.append(1)
numbers.append(2)
numbers.append(3)

// Array 요소에 Index 값으로 접근하기
numbers[0]
numbers[1]


// Array 안에 중간에 insert 하기 = 숫자 4를 index 2 번 자리에 넣기
numbers.insert(4, at: 2)
numbers

// Array 안에 값 index 0 번 자리에서 지우기
numbers.remove(at: 0)
numbers

// 출력된 문법으로 Array 를 생성하기
var names:[String] = []

Dictionary 예제

// Dictionary 만들기
var dic: Dictionary<String, Int> = Dictionary<String, Int>()
// 축약된 형태로 Dictionary 만들기 = 초기값을 선언해서 만들수 있음
var dic2: [String: Int] = ["Jacob": 1]

// Dictionary 에 값 추가 하기
dic2["Emma"] = 3
dic2["John"] = 5
dic2

// Dictionary 에 값 변경하기
dic2["Jacob"] = 6
dic2

// Dictionary 값 제거하기
dic2.removeValue(forKey: "Jacob")
dic2

Set 예제

// 순서와 상관없이 data 의 중복을 허용하지 않는 Set 만들기 = Set 는 축약형 으로 사용하는 방법은 없기 때문에 아래와 같이만 사용해야 함
var set: Set = Set<Int>()

// Set 에 값을 넣기
set.insert(10)
set.insert(20)
set.insert(30)
set.insert(30) // inserted false 됨 중복되기 때문에
set

// Set 안에 값 지우기
set.remove(20)
set

4.함수

함수는 작업의 가장 작은 단위이자 코드의 집합입니다 : 반복적인 프로그래밍을 피하기 위해서 사용합니다

  • 함수의 기본적인 형태
func 함수명(파라미터 이름: 데이터 타입) -> 반환 타입 {
  return 반환 
}
import UIKit

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

// 함수 생성
func sum(a: Int, b:Int) -> Int {
	return a + b
}
// 함수 호출
sum(a: 5, b: 3)

// 문자열 출력
func hello() -> String {
	return "hello"
}
hello()

// 반환값이 없는 함수작성
func printName() {

}

// me 값으로 Jacob 이라는 기본값을 사용할 수 있음
func greeting(friend: String, me: String = "Jacob") {
	print("Hello, \(friend)! I'm \(me)")
}

greeting(friend: "Emma")



// 전달인자 label 을 사용해서 함수 만들기

/*
 func 함수 이름(전달인자 레이블: 매개변수 이름: 매개변수 타입, 전달인자 레이블: 매개변수 이름: 매개변수 타입...) -> 반환 타입 {
		retrun 반환 값
 }

 */

func sendMessage(from myName: String, to name: String) -> String {
  return "Hello \(name)! I'm \(myName)"
}

sendMessage(from: "Jacob", to: "Emma")

// 전달 인자 label 을 사용하지 않을경우 wildcard 식별자를 사용함 : _ 를 앞에다 붙여 줌
func sendMessage2(_ name: String) -> String {
	return "Hello \(name)!"
}
sendMessage2("Jacob")


// 몇개의 값이 변수로 들어올지 모를때 가변 매개변수를 사용하는 함수 : 배열처럼 사용이 가능, 함수마다 가변 매개변수는 한가지만 가질 수 있음
func sendMessage3(me: String, friends: String...) -> String {
	return "Hello \(friends)! I'm \(me)"
}
// 여러개의 문자열을 넘기는데 array 안에 3명이 들어감
sendMessage3(me: "Jacob", friends: "Emma", "Json", "John")

5.조건문

주어진 조건에 따라서 어플리케이션을 다르게 동작하도록 하는것

if

/*
 if 조건식 {
	실행할 구문
 }
 */

let age = 20
if age < 19 {
	print("미성년자 입니다")
}

/*
 if 조건식 {
	 조건식이 만족하면 해당 구문 실행
 } else {
	 만족하지 않으면 해당 구문 실행
 }
 */

if age < 19 {
	print("미성년자")
} else {
	print("성년자")
}


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

let animal = "강아지"

if animal == "dog" {
	print("강아지 사료주기")
} else if animal == "cat" {
	print("고양이 사료주기")
} else {
	print("해당하는 동물 사료가 없음")
}

switch

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

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


// switch 문을 사용해서 숫자 범위 -20 에서 9도까지는 겨울입니다 라고 나타 내는 것
let temp = 30
switch temp {
case -20...9:
	print("겨울 입니다")
case 10...14:
	print("가을 입니다")
case 15...25:
	print("봄 입니다")
case 26...35:
	print("여름 입니다")
default:
	print("이상기온 입니다")
}

6.반복문

반복적으로 코드가 실행되게 만드는 구문

for-in

/*
 for 루프 상수 in 순회 대상 {
	// 실행할 구문..
 }
 */

for i in 1...4 {
	print(i)
}

let arr = [1, 2, 3, 4, 5]

for i in arr {
	print(i)
}

while

/*
 while 조건식 {
	// 실행할 구문
 }
 */

var num = 5

while num < 10 {
	num += 1
}

num

repeat-while

/*
	repeat {
	// 실행할 구문
 } while 조건식
 */


var x = 6

repeat {
	x += 2
} while x < 5

// 조건에 맞지 않아도 한번은 실행되기 때문에 +2 가 되서 8이 출력이 됨
print(x)

7.옵셔널

값이 있을 수도 있고 없을 수도 있는 것임

nil 은 dart 언어나 다른 언어에서의 null 을 가리킴 var name: String? = nil

  • optional nil 을 사용할때는 type 뒤에 ? 를 붙여 줘서 사용해야 함
var name: String?

// optional 변수에 초기값으로 값을 넣을 수 있음
var optionalName: String? = "Jacob"
print(optionalName) // 출력값으로 Optional("Jacob") 이라고 optional 값으로 출력되기때문에 재대로 실행하려면 optional binding 이 필요함

Optional binding

  • 명시적 해제 : 강제 해제, 비강제 해제 (옵셔널 바인딩)

    • 변수 명 뒤에 ! (느낌표) 를 붙여서 optional 을 강제로 해제 시키는데, 이방법을 사용하게 되면 강제로 프로그램 error 가 발생 할 수도 있음

    • 안전하게 해제 하기 위해서는 비강제 해제 방식 사용

  • 묵시적 해제 : 컴파일러에 의한 자동 해제, 옵셔널의 묵시적 해제

var name: String?

// optional 변수에 초기값으로 값을 넣을 수 있음
var optionalName: String? = "Jacob"
print(optionalName)
// optional 강제 해제
print(optionalName!)

// optional 비강제 해제
if let result = optionalName {
	print(result)
} else {

}


// 묵시적으로 해제 optional 을 ? 대신 ! 을 사용해서 optional 을 해제시킴
let string = "12"
var stringToInt: Int! = Int(string)
print(stringToInt + 1)

8.구조체

프로퍼티와 메소드를 사용해서 구조화된 data 와 기능을 가질 수 있는 하나의 사용자 정의 데이터 타입을 만드는 것임

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

// structure 생성
struct User {
	var nickname: String
	var age: Int

	// struct 안에 method 정의
	func information() {
		print("\(nickname) \(age)")
	}
}

// instance 생성
var user = User(nickname: "Jacob", age: 30)

// 실행
user.nickname
// user nickname 변경
user.nickname = "John"
user.nickname


// struct 의 method 실행
user.information()

9.클래스

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

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

	// class 에서는 constructor 를 생성해줘야 함 초기화 값임
	init() {

	}

	// method 생성
	func introduce() {
		print("name \(name) age \(age)")
	}
}

// class 의 instance 생성
var dog = Dog()

dog.name = "coco"
dog.age = 3
dog.name
dog.age

dog.introduce()

10.초기화(initialization)

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

/*
	init(매개변수: 타입, ...) {
	// 프로퍼티 초기화
	ㅁㄴㅇㄴ
 }
 */

// 클래스 선언
class User {
	var nickname: String
	var age: Int

	// 초기화 dart 나 Js 에서 this 가 self 로 쓰임
	init(nickname: String, age: Int) {
		self.nickname = nickname
		self.age = age
	}

	// 초기값에 기본값 설정
	init(age: Int) {
		self.nickname = "Emma"
		self.age = age
	}

	// deinitialize: instance 가 메모리에 해제되기 직전에 호출되고 클래스 인스턴스와 관련해서 정의 작업을 할 수 있음
	deinit {
		print("deinit user")
	}

}

// User instance 생성
var user = User(nickname: "Jacob", age: 30)

user.nickname
user.age

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

// Swift 는 instance 가 더이상 필요하지 않으면 자동으로 소멸을 시켜 버림 user3 에 nil 을 대입 시키면 더이상 필요 없다고 판단함
var user3: User? = User(age: 23)
user3 = nil

11.프로퍼티

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

    • 저장 프로퍼티: 인스턴스의 변수, 상수를 의미

    • 연산 프로퍼티: 연산의 값을 저장하는 것이 아니라 특정 연산값을 실행하는 것을 의미함

    • 타입 프로퍼티: 특정 인스턴스에서 사용되는것이 아닌

import Foundation

// 저장 프로퍼티

// 구조체
struct Dog {
	var name: String
	let gender: String
}


var dog = Dog(name: "Jacob", gender: "Male")
print(dog)

dog.name = "제이콥"

// dog2 는 let 으로 선언되었기 때문에 변경 프로퍼티 변경 안됨
let dog2 = Dog(name: "Emma", gender: "female")

// 구조체는 value type 이기 때문에 상수로 선언하게 되면 변수로 선언된 property 라고 해도 값이 변경이 안됨
// 단 클래스의 reference type 이여서 구조체와 다르게 클래스 instance 는 상수로 선언을해도 변수로 선언된 property 의 값을 바꿀 수 있음


// 클래스
class Cat {
	var name: String
	let gender: String

	// intialization 생성
	init(name: String, gender: String) {
		self.name = name
		self.gender = gender
	}
}

// 상수로써 instance 선언
let cat = Cat(name: "json", gender: "male")
cat.name = "Jacob"
print(cat.name)


// 연산 프로퍼티 : 저장 프로퍼티는 구조체와 클래스에서만 사용되지만, 연산 프로퍼티는 구조체, 클래서, 열거형에서도 사용이 가능함
// 연산 프로퍼티는 값을 직접적으로 저장하지는 않지만, getter, setter 를 사용해서 다른 property 와의 값들을 직접적으로 접근할 수 있게 됨

struct Stock {
	var averagePrice: Int
	var quantity: Int
	var purchasePrice: Int {
		get {
			return averagePrice * quantity
		}
		// set 에 property 값을 입력하지 않으면 newVal 의 값이 기본값으로 설정이 됨
		set(newPrice) {
			averagePrice = newPrice / quantity
		}
	}
}

var stock = Stock(averagePrice: 2300, quantity: 3)
print(stock)

// 연산형 프로퍼티 접근
stock.purchasePrice // 평균값 6900
stock.purchasePrice = 3000 // purchasePrice 에서 3000 으로 바꾸미
stock.averagePrice // averageProce : 1000


// 프로퍼티 옵져버: 프로퍼티의 값의 변화를 관찰하고 반영함 새로운 값이 같다고 하더라도, 프로퍼티가 set 될때마다 호출 된다고 보면 됨
// 프로퍼티 옵져버는 3가지 경우에만 사용이 가능: 저장 프로퍼티, overriding 이 된 계산 프로퍼티에서만 사용이 가능함

class Account {
	var credit: Int = 0 {
		// 프로퍼티 옵저버에는 값이 저정되기 직전에 호출되는 willSet 옵져버 : 새로 저장될 property 의 값을 상수 매개변수로 전달을 함 만약 지정하지 않으면 newValue 의 값이 매개변수의 값이 됨
		willSet {
			print("잔액이 \(credit)원에서 \(newValue) 원으로 변경될 예정입니다")
		}
		// 값이 저장된 직후에 호출되는 didSet 옵져버: property 의 기존값이 상수 매개변수로 전달되어짐 이 매개변수의 이름은 didSet 구문안에 사용될 수 있도록 지정할 수 있음. 만약 지정하지 않으면 기본값인 oldValue 가 매개 값이 됨
		didSet {
			print("잔액이 \(oldValue) 원에서 \(credit) 원으로 변경되었습니다")
		}
	}
}

// account instance
var account = Account()

account.credit = 1000 // 잔액이 0원에서 1000 원으로 변경될 예정입니다
// 잔액이 0 원에서 1000 원으로 변경되었습니다


// Type Property : instance 생성없이 객체 내에 property 에 접근이 가능하게 하는것인데, 프로퍼티 타입 자체와 연결하는 것을 말함 static 키워드를 사용해서 정의함

struct SomeStructure {
	static var stroedTypeProperty = "Some value." // 스토어
	static var computedTypeProperty: Int {
		return 1
	}
}

// instance 를 생성안해도 type property 는 타입 이름과 프로퍼티 이름을 연결해서 바로 사용 가능
SomeStructure.stroedTypeProperty
SomeStructure.computedTypeProperty

12.클래스와 구조체의 차이

Class 와 structure 의 공통점

  • 값을 저장할 프로퍼티를 선언할 수 있습니다.

  • 함수적 기능을 하는 메서드를 선언 할 수 있습니다.

  • 내부 값에 .을 사용하여 접근할 수 있습니다.

  • 생성자를 사용해 초기 상태를 설정할 수 있습니다

  • Protocol 을 채택하여 기능을 설정할 수 있습니다.

차이점

Class

  • 참조타입

  • ARC 로 메모리 관리

  • 상속이 가능

  • 타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입을 확인할 수 있음

  • deinit 을 사용해서 클래스 인스턴스의 메모리 할당을 해제할 수 있음

  • 같은 클래스 인스턴스를 여러개의 변수에 할당한 뒤 값을 변경 시키면 모든 변수에 영향을 줌 (메모리가 복사 됨)

Structure

  • 값 타입

  • 구조체 변수를 새로운 변수에 할당 할 때마다 새로운 구조체가 할당 됩니다.

  • 즉 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시키더라도 다른 변수에 영향을 주지 않음 (값 자체를 복사)

// Class vs Struct

class SomeClass {
	var count: Int = 0
}

struct SomeStruct {
	var count: Int = 0
}

var class1 = SomeClass()
var class2 = class1
var class3 = class1


class3.count = 2
class1.count // class1 의 값이 2로 변경됨 왜냐면 class 는 참조 타입이기 때문에 같은 class 의 instance 를 할당하게 되면 참조된 instance 의 값이 같이 변경이 됨 즉, 변수를 복사하더라도 하나의 instance pointer 를 가리키기 때문에 원본, 복사본이 모두 같은 값을 가지게 됨

// 구조체는 다르게 값 타입이기때문에 복사하더라도 원본이 바뀌지 않음

var struct1 = SomeStruct()
var struct2 = struct1
var struct3 = struct2

struct2.count = 3
struct3.count = 4

struct1.count // 0
struct2.count // 3
struct3.count // 4
// 즉 구조체는 각각 다른 값을 가지게 됨. 왜냐면 구조체는 값 타입이라서 구조체 instance 를 할당하더라도 매번 새로운 메모리가 할당되어서 값을 변경하더라도 구조체에 영향을 주지 않음

13.상속

  • class 가 다른 클래스로 부터 method, property 등을 상속받아서 사용하는 특성. 상속 받는 class 를 child class, 상속 주는 class 는 super class 라고 합니다

class Vehicle {
	var currentSpeed = 0.0
	var description: String {
		return "Traveling at \(currentSpeed) miles per hour "
	}
	func makeNoise() {
		print("Speaker on")
	}
}


// 상속
/*
 class 클래스 이름: 부모클래스 이름 {
		// 하위 클래스 정의
 }
 */


class Bicycle: Vehicle {
	var hasBasket = false
}

// bicycle 인스턴스 생성
var bicycle = Bicycle()
bicycle.currentSpeed // 0 출력
bicycle.currentSpeed = 15.0
bicycle.currentSpeed // 15 출력


// override

class Train: Vehicle {
	// makeNoise 를 override 하기 (기능을 재정의)
	override func makeNoise() {
		super.makeNoise() // 자식 class 에서 makeNoise 가 호출될때 super class 에서 먼저 makeNoise() 를 실행함
		print("choo choo")
	}
}

var train = Train()
train.makeNoise() // Speaker on choo choo 출력


// 프로퍼티를 overring 하기

class Car: Vehicle {
	var gear = 1
	override var description: String {
		return super.description + "in gear \(gear)"
	}
}

var car = Car()
car.currentSpeed = 30.0
car.gear = 2
print(car.description) // Traveling at 30.0 miles per hour in gear 2


// overring 된 상태에서 property obsever 를 추가하기
// 상속된 property 에 overring 을 사용하면 property 옵져버를 추가 할 때는 상수저장, read-only 저장 프로퍼티는 프로퍼티 옵져버를 추가 할 수 없음. 그 이유는 값을 설정할 수 없기 때문에 willSet 이나, didSet 을 사용할 수 없기 때문임

class AutomaticCar: Car {
	override var currentSpeed: Double {
		didSet {
			gear = Int(currentSpeed / 10) + 1
		}
	}
}

var automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")  // AutomaticCar: Traveling at 35.0 miles per hour in gear 4


// 타입 앞에 final 을 작성하게 되면 overriding 이 되지 않음

14.타입 캐스팅

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

  • 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: "Butter", artist: "BTS"),
	Movie(name: "올드보이", director: "박찬욱"),
	Song(name: "Wonderful", artist: "Oasis"),
	Song(name: "Rain", artist: "이적")
]

var movieCount = 0
var songCount = 0

for item in library {
	if item is Movie {
		movieCount += 1
	} else if item is Song {
		songCount += 1
	}
}

print("Media library contains \(movieCount) movies and \(songCount) songs") // Media library contains 2 movies and 3 songs

// 타입 변환
for item in library {
	if let movie = item as? Movie {
		print("Movie: \(movie.name), dir. \(movie.director)")
	} else if let song = item as? Song {
		print("Song: \(song.name), by \(song.artist)")
	}
}

/*
 Movie: 기생충, dir. 봉준호
 Song: Butter, by BTS
 Movie: 올드보이, dir. 박찬욱
 Song: Wonderful, by Oasis
 Song: Rain, by 이적
 */


// as? 는 optional 로 타입 변형을 하는것이고, as! 을 사용하면 확실하게 강제로 타입 변환을 하는것임(! 을 사용해서 잘못된 class type 으로 변경 하면 runtime error 발생되서 프로그램 강제 종료됨)

15.assert 와 guard

  • assert

    - 특정 조건을 체크하고, 조건이 성림되지 않으면 메세지를 출력하게 할 수 있는 함수
    
    - assert 함수는 디버깅 모드에서만 동작하고 주로 디버깅 중 조건의 검증을 위하여 사용합니다
    
  • guard 문

    - 뭔가 검사하여 그 다음에 오는 코드를 실행할지 말지 결정 하는것
    
    - guard 문에 주어진 조건문이 거짓일 때 구문이 실행됨
    
    - 주로 early exit 이라고 해서 guard 문을 사용해서 조건을 만족하지 않으면 (false 일때,) code 를 실행하지 않도록 하는 방어코드로 사용을 주로 합니다.
    
// assert : 조건의 검증을 위해서 사용함

var value = 0
assert(value == 0)

value = 2
// assert(value == 0, "값이 0이 아닙니다") // Assertion failed : 값이 0이 아닙니다


// guard

/*
guard else {
  // 조건이 false 이면 else 구문이 실행되고
 return or throw or break 을 통해 이 후 코드를 실행하지 않도록 함
 }
 */

// value 가 0일 때만 안녕하세요 실행되고 아닐때는 guard 문 이 실행되어 return 됨
func guardTest(value1: Int) {
	guard value1 == 0 else { return }
	print("안녕하세요")
}

guardTest(value1: 0)

16.Protocol

특정 역활을 하기 위한 method, property, 기타 요구사항등의 청사진을 말함

// protocol

/*
 protocol 이름 {
	// 프로토콜 요구사항
 }
 */

// get, set 을 사용해서 읽기만 가능한지, 쓰기와읽기가 가능한지 설정 가능
protocol FirstProtocol {
	var name: Int {get set}
	var age: Int {get}
}

protocol AnotherProtocol {
	static var someTypeProperty: Int {get set}
}

protocol FullNames {
	var fullName: String {get set}
	func printFullName()
}

protocol SomeProtocol3 {
	func someTypeMethod()
}


// struct 에서  protocol 채택 (다수일때 아래와 같이 , 로 구분)
struct Person: FullNames {
	var fullName: String

	func printFullName() {
		print(fullName)
	}
}

// class 에서 protocol 채택 (상속받을 superClass 를 먼저 쓰고, 프로토콜을 순서대로 작성함)
/*
 class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol{

 }
 */
// protocol initializer 요구사항 : protocol 은 자신을 체택한 타입에 생성자도 요구할 수 있음

protocol SomeProtocol4 {
	init(someParameter: Int)
}

protocol SomeProtocol5 {
	init()
}

class SomeClass: SomeProtocol5 {
	required init() {

	}
}

17.Extensions

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

  • extension 이 type 에 추가 할 수 있는 기능

    - 연산 타입 프로퍼티 / 연산 인스턴스 프러퍼티
    
    - 타입 메서드 / 인스턴스 메서드
    
    - 이니셜라이저
    
    - 서브스크립트
    
    - 중첩 타입
    
    - 특정 프로토콜을 준수할 수 있도록 기능 추가
    

// Extension

/*
	extension SomeType {
	// 추가 기능
 }
 */

// Int 형태의 타입이 짝수인지 홀수 인지 판단하는 extension 생성
extension Int {
	var isEven: Bool {
		return self % 2 == 0
	}

	var isOdd: Bool {
		return self % 2 == 1
	}
}

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


// type 에 method 추가하기
extension String {
	func converToInt() -> Int? {
		return Int(self)
	}
}

var str = "0"
str.converToInt() // Int 형 타입으로 0 이 변경되서 출력됨

18.Enum

연관성이 있는 값을 모아 놓은 것을 말함

// enum

// enum 의 각 항목은 그 자체로의 고유값임
enum CompassPoint {
	case north
	case south
	case east
	case west
}

// enum 을 만들게 되면 하나의 type 처럼 사용 할 수 있음

var direction = CompassPoint.east // east
direction = .west // west 로 변경됨

// 열거형은 switch 구문과 함께 쓰면 다양하게 활용 할 수 있음

switch direction {
case .north:
	print("북으로 가라~")

case .east:
	print("동쪽으로 가라")
case .south:
	print("남으로 가라")

case .west:
	print("서쪽으로 가라")
} // 서로쪽으로 가라 출력


// 특정 type 을 원시 값으로 가지게 하기: enum 을 사용해서 type 을 명시해 주기
// 각 항목에 초기값 설정
enum CompassPoint2: String {
	case north = "북"
	case south = "남"
	case east = "동"
	case west = "서"
}

// 선언된 초기값을 사용하려면 rawValue 를 통해서 원시값을 가져 올 수 있음

var direction2 = CompassPoint2.east

switch direction2 {
case .north:
	print(direction2.rawValue)

case .east:
	print(direction2.rawValue)
case .south:
	print(direction2.rawValue)

case .west:
	print(direction2.rawValue)
} // 동 출력됨


// 원시값을 가지고 enum 을 출력하기
let direction3 = CompassPoint2(rawValue: "남") // direction3 는 south 값을 가지게 됨


// 연관값 을 추출해서 사용하기
enum PhoneError {
	case unknown
	case batteryLow(String)
}

let error = PhoneError.batteryLow("배터리가 곧 방전됩니다.")

switch error {
case .batteryLow(let message):
	print(message) // message 문으로 배터리가 곧 방전됩니다 가 출력됨

case .unknown:
	print("알수 없는 에러 입니다")
}

19.Optional chaining

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

// optional chaining

struct Developer {
	let name: String
}

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

var developer = Developer(name: "Emma")
var company = Company(name: "Jacob", developer: developer)
print(company.developer) // Optional(__lldb_expr_37.Developer(name: "Emma")) 출력됨

// optional chaining
print(company.developer?.name) // Optional("Emma")
print(company.developer!.name) // Emma

20.try-catch

에러 처리를 위한 try-catch 문 : 프로그램 내에서 에러가 발생한 상황에 대해 대응하고 이를 복구 하는 과정

runtime 과정에서 error 처리를 위한 단계는 4가지가 있습니다

  • throwing(발생)

  • catching(감지)

  • propagating(전파)

  • manipulating(조작)

import Foundation

// try-catch

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

// throw 를 사용해서 error 를 발생 시키기
// throw PhoneError.batteryLow(batteryLevel: 20)


// 함수에서 발생한 error 를 해당 함수에 전파하는 방법
func checkPhoneBatteryStatus(batteryLevel: Int) throws  -> String{
	guard batteryLevel != -1 else { throw  PhoneError.unknown}
	guard batteryLevel > 20 else { throw PhoneError.batteryLow(batteryLevel: 20)}
	return "배터리 상태가 정상입니다."
}

/*
 do {
	try 오류 발생 가능 코드
 } catch 오류 패턴 {
	처리 코드
 }
 */


do {
	try checkPhoneBatteryStatus(batteryLevel: 20)
} catch PhoneError.unknown {
	print("알 수 없는 에러입니다.")
} catch PhoneError.batteryLow(let baterryLabel) {
	print("배터리 전원 부족 남은 배터리 : \(baterryLabel) %")
} catch {
	print("그 외 오류 발생 : \(error)")
} // 배터리 전원 부족 남은 배터리 : 20 % 출력됨

let status = try? checkPhoneBatteryStatus(batteryLevel: 30)
print(status) // nil


// 오류가 절대 발생되지 않을것이라고 해서 ! 을 사용하는 경우
let status2 = try! checkPhoneBatteryStatus(batteryLevel: 30)
print(status2) //


21.Closer

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

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

// 클로저 표현식

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

let hello = {() -> () in
	print("hello")
}

hello()

// parameter 와 return type 이 있는 클로저
let hello2 = { (name: String) -> String in
	return "Hello, \(name)"
}

hello2("Jacob")

// 클러저를 함수의 parameter 로 전달하는 함수
func doSomething(closure: () -> ()) {
	closure()
}


// doSomething(closure: { <#T##() -> ()#> in
// 	print("hello")
// })

// 클로져를 반환 하는 type
func doSomething2() -> () -> () {
	return { () -> () in
		print("hello4")
	}
}
doSomething2()() // hello4


// 후행 클로저: 읽기 쉽게 형태를 바꾸는 것인데, 맨 마지막에 매개변수로 전달되는 클로져에만 해당 되므로, 매개변수 클로져를 여러개 전달할때는 마지막 매개변수로만 후행 클로저를 사용할 수 있다
doSomething {
	print("hello2")
}

// 다중 후행 클로져 문법
func doSomething2(success: () -> (), fail: ()-> ()) {

}

doSomething2 {
	<#code#>
} fail: {
	<#code#>
}


22.고차함수

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

swift 에서 제공하는 고차함수로는 map, filter, reduce 등이 있습니다.

// map
// container 내의 data 를 변형하여 새로운 container 를 return 함
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
// container 의 내부의 값을 걸러서 다른 container 로 추출 하는 역활을 함
let intArray = [10, 5, 20, 13, 4]
let filterArray = intArray.filter{ $0 > 5}
print("filter: \(filterArray)") // filter: [10, 20, 13]

// reduce
// container 요소를 하나로 통합할때, 연산자 한개의 value 만 return 할 수 있음
// 총합을 구할 때 자주 사용
let someArray = [1, 2, 3, 4, 5]
// 첫번째 매개변수를 0으로 설정하고, 매개변수
let reduceResult = someArray.reduce(2) {
	(result: Int, element: Int) -> Int in
	print("\(result) + \(element)")
	return result + element
}
print("reduce: \(reduceResult)")

🔶 🔷 📌 🔑

Reference

inswave - http://tech.inswave.com/2018/04/02/Swift/

[Swift 문법] Swift 공식 문서 정리 - https://icksw.tistory.com/2

fastcampus - https://fastcampus.co.kr/dev_online_iosappfinal

Categories:

Updated:

Leave a comment