Swift
1. 소개
2. PlayGround 란?
3. Swift에 대해 알아보자
Safe, Fast, Expressive
4. 상수와 변수
상수: 변하지 않은 일정한 값을 갖는다.
변수: 변할 수 있는 값을 갖는다.
5. 데이터타입
- Int : 64bit 정수형
- UInt: 부호가 없는 64bit 정수형
- Float: 32bit 부동 소수점
- Double: 64Bit 부동 소수점
- Bool: true, false 값
- Character: 문자
- String: 문자열
- Any: 모든 타입을 지칭하는 키워드
// Int
var somInt: Int = -100
somInt = 100
// UInt
var somUInt: UInt = 200
// Float
var someFloat: Float = 1.1
someFloat = 1
print(someFloat) // 1.0 이 출력
// Dobule
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: 10
6. 컬랙션 타입
컬랙션 타입이란?
컬랙션 타입은 데이터들의 집합 묶음
Array
데이터 타입의 값들을 순서대로 지정하는 리스트
Dictionary
순서없이 키(Key)와 값(Value) 한 쌍으로 데이터를 저장하는 컬랙션 타입
Set
같은 데이터 타입의 값을 순서없이 저장하는 리스트 (중복된 값을 가질수 없음)
// Array
var numbers: Array<Int> = Array<Int>()
numbers.append(1)
numbers.append(2)
numbers.append(3)
numbers[0]
numbers[1]
numbers[2]
numbers.insert(4, at: 2)
numbers
numbers.remove(at: 0)
numbers
var names: [String] = []
// Dictionary
var dic: Dictionary<String, Int> = Dictionary<String, Int>()
var dic: [String: Int] = ["권태완" : 1]
dic["김철수"] = 3
dic["김민지"] = 5
dic
dic["김민지"] = 6
dic
dic.removeValue(forKey: "김민지")
dic
// Set
var set: Set = Set<Int>()
set.insert(10)
set.insert(20)
set.insert(30)
set.insert(30)
set.insert(30)
set
set.remove(20)
set
7. 함수 사용법
함수는 작업의 가장 작은 단위이자 코드의 집합입니다.
함수의 정의와 호출
/*
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() -> Void {
}
func greeting(friend: String, me: String = "gunter") {
print("Hello, \(friend)! I'm \(me)")
}
greeting(friend: "Albert")
/*
func 함수 이름(전달인자 레이블: 매개변수 이름: 매개변수 타입, 전달인자 레이블: 매개변수 이름: 매개변수 타입...) -> 반환타입{
return 반환갑
}
*/
func sendMessage(from myName: String, to name: String) -> String {
return "Hello \(name)! I'm \(myName)"
}
sendMessag(from: "Gunter", to:"Json") // "Hello Json! I'm Gunter"
// 전달인자 레이블 생략
func SendMessage(_ name: String) -> String {
return "Hello \(name)"
}
sendMessage("권태완")
// 가변매개변수
func sendMessage(me: String, friends: String...) -> String {
return "Hello \(friends)! I'm \(me)"
}
sendMessage(me: "Gunter", friends: "Json", "Albert", "Stella")
8. 조건문
주어진 조건에 따라서 애플리케이션을 다르게 동작하도록 하는것이다.
/*
if 조건식 {
실행할 구문
}
*/
let age = 12
if age < 19 {
print("미성년자 입니다.")
}
/*
if 조건식 {
조건식이 만족하면 해당 구문 실행
} else {
만족하지 않으면 해당 구문 실행
}
*/
if age < 19 {
print("미성년자")
} else {
print("성년자")
}
/*
if 조건식 {
조건식이 만족하면 해당 구문 실행
} else if 조건식 2 {
조건식 2를만족하면 해당 구문 실행
} else {
// 아무 조건식도 만족하지 않을 때 실행할 구문
}
*/
let animal = "cat"
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 "yellow":
print("노란색 입니다.")
default
print("착는 색상이 없습니다. ")
}
// 범위연산자 사용
let temperature = 30
switch temperature {
case -20...9:
print("겨울 입니다.")
case 10...14:
print("가을 입니다.")
case 15...25:
print("봄 입니다.")
case 26...35:
print("여름 입니다.")
default:
print("이상기후입니다.")
}
9. 반복문
반복적으로 코드가 실행되게 만드는 구문을 말한다.
/*
for 루프상수 in 순회대상 {
// 실행할 구문..
}
*/
for i in 1...4 {
print(i)
}
let array = [1,2,3,4,5]
for i in array {
print(i)
}
/*
while 조건식 {
// 실행할 구문
}
*/
var number = 5
while number < 10 {
number+=1
}
number
/*
repat {
// 실행할 구문
} while 조건식
*/
var x = 6
repeat {
x+=2
} whle x< 5
print(x) // x = 8
// 조건식에 따라 반복이 실행되지만 적어도 한번은 구문을 실행함
10. 옵셔널
값이 있을수도 없을수도 있음.
var name: String?
var optionalName: String? = "Gunter"
var requiredName: String = optionalName // 에러발생
-
옵셔널 바인딩
-
명시적 해제 : 강제 해제, 비강제 해제( 옵셔널 바인딩)
- 묵시적 해제 : 컴파일러에 의한 자동 해제, 옵셔널의 묵시적 해제
// 명시적 해제
var number: Int? = 3
print(number)
print(number!) // 강제해제
if let result = number { // 비강제 해제
print(result)
} else {
// nil 일경우
}
func test() {
let number: Int? = 5
guard let result = number else { return }
print(result)
}
test()
// 비교연산자를 비교하면 자동적으로 옵셔널을 해제
let valute = Int? = 6
if value == 6 {
print("value가 6입니다.")
} else {
print("value가 6이 아닙니다. ")
}
let string = "12"
var stringToInt: Int! = Int(string) // 묵시적 옵셔널 해제
12. 구조체
구조체의 정의
/*
struct 구조체 이름 {
프로퍼티와 메서드
}
*/
struct User {
var nickname: String
var age: Int
func information() {
print("\(nickname) \(age)")
}
}
var user = User(nickname: "gunter", age: 23)
user.nickname
user.nickname = "ablert"
user.nickname
user.information()
13. 클래스
클래스의 정의
/*
class 클래스 이름 {
프로퍼티와 메서드
}
*/
class Dog {
var name: String = ""
var age: Int = 0
init() {
}
func introduce() {
print("name \(name) age \(age)")
}
var dog = Dog()
dog.name = "Coco"
dog.age = 3
dog.name
dog.age
dog.introduce()
14. 초기화 구문 init
초기화(Initailization)란?
클래스 구조체 또는 열거형의 인스턴스를 사용하기 위한 준비과정
/*
init (매개변수: 타입, ...) {
// 프로퍼티 초기화
// 인스턴스 생성시 필요한 설정을 해주는 코드 작성
}
*/
class User {
var nickname: String
var age: Int
init(nickname: String, age: Int) {
self.nickname = nickname
self.age = age
}
init(age: Int) {
self.nickanme = "ablert"
self.age = age
}
deinit {
print("deinit User")
}
}
var user = User(nickname: "gunter", age: 23)
user.nickname
user.age
var user2 = User(age: 23)
user2.nickname
user2.age
var user3 = User(age: 23)
user3 = nil // deinit User 출력됨
15. 프로퍼티
프로퍼티 종류
- 저장 프로퍼티
- 연산 프로퍼티
- 타입 프로터피
struct Dog {
var name: String
var gender: String
}
var dog = Dog(name: "gunter", gender: "Male") // 저장프로퍼티
print(dog)
dog.name = "권태원"
let dog2 = Dog(name: "gunter", gender: "male")
// struct 는 Value 타입이기때문에 상수로 선언하게되면 변수로 선언된 프로퍼티라 하더라도 변경이 안된다.
class Cat {
var name: String
let gender: String
init (name: String, gender: String) {
self.name = name
self.gender = gender
}
}
let cat = Cat(name: "json", gender: "male")
cat.name = "gunter"
print(cat.name)
// class 는 참조타입이기 때문에 변수로 선언된 프로퍼티의 값이 변경됨
// 계산 프로퍼티
struct Stock {
var averagePrice: Int
var quantity: int
var purchasePrice: Int {
get {
return averagePrice * quantity
}
set(newPrice) {
averagePrice = newPrice / quantity
}
}
}
var stock = Stock(averagePrice: 2300, quantity: 3)
print(stock)
stock.purchasePrice
stock.purchasePrice = 3000
stock.purchasePrice
// 저장 프로퍼티
class Account {
var credit: Int = 0 {
// 소괄호 이름 지정
willSet {
print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
}
didSet {
print("잔액이 \(oldValue)원에서 \(credit)원으로 변경되었습니다.")
}
}
}
var account = Account()
account.credit = 1000
// 타입프로퍼티
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
SomeStructure.computedTypeProperty
SomeStructure.storedtypeProperty
SomeStructure.storedtypeProperty = "hello"
SomeStructure.storedtypeProperty
16. 클래스와 구조체의 차이
공통점
- 값을 저장할 프로퍼티를 선언할 수 있습니다.
- 함수적 기능을 하는 메서드를 선언 할 수 있습니다.
- 내부 값에 . 을 사용하여 접근할 수 있습니다.
- 생성자를 사용해 초기 상태를 설정할 수 있습니다.
- extension을 사용하여 기능을 확장할 수 있습니다.
- Protocol을 채택하여 기능을 설정할 수 있습니다.
차이점
클래스 | 구조체 |
---|---|
참조타입 | 값타입 |
ARC로 메모리를 관리 | 구조체 변수를 새로운 변수에 할당할 때마다 새로운 구조체가 할당됩니다. |
상속이 가능 | 즉 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시키더라도 다른 변수에 영향을 주지 않음 (값 자체를 복사) |
타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입을 확인할 수 있음 | |
deinit을 사용하여 클래스 인스턴스의 메모리 할당을 해제할 수 있음 | |
같은 클래스 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경 시키면 모든 변수에 영향을 줌 (메모리가 복사 됨) |
class SomeClass {
var count: Int = 0
}
struct SomeSturct {
var count: Int = 0
}
var class1 = SomeClass()
var class2 = class1
var class3 = class1
class3.count = 2
class1.count // 2
// 변수를 복사하더라도 하나의 주소를 가리키기 때문은 값이 같게됨
var struct1 = SomeStruct()
var struct2 = struct1
var struct3 = struct1
struct2.count = 3
struct3.count = 4
struct1.count // 0
struct2.count // 3
struct3.count // 4
// 매번 새로운 메모리에 할당되기때문에
17. 상속
클래스가 다른 클래로부터 메소드, 프로퍼티 또는 다른 특성들을 상속받는것을 말한다.
class Vehicle {
var currentSpped = 0.0
var descrpition: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
print("speaker on")
}
}
/*
class 클래스이름: 부모클래스이름 {
// 하위 클래스 정의
}
*/
class Bicycle: Vehicle {
var hasBasket = false
}
var bicycle = Bicycle()
bicycle.currentSpped
bicycle.currentSpped = 15.0
bicycle.currentSpped
// 오버라이드
class Train: Vehicle {
override func makeNoise() {
super.makeNoise()
print("choo choo")
}
let train = Train()
train.makeNoise()
// 프로퍼티 오버라이드
class Car: Vehicle {
var gear = 1
override var description: String {
retrun super.description = " in gear \(gear)"
}
}
let car = Car()
car.currentSpeed = 30.0
car.gear = 2
print(car.description)
class AutomaticCar: Car {
override var currentSpped: Double {
didSet {
gear = Int(currentSpped / 10) + 1
}
}
}
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)") // AutomaticCar: traveling at 35.0 miles per hour in gear 4
18. 타입캐스팅
인스턴스의 타입을 확인하거나 어떠한 클래스의 인스턴스를
해당 클래스 계층 구조의 슈퍼 클래스나 서브 클래스로 취급 하는 방법
is
, as
연산자를 사용
is 연산자 사용
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: "Wonderwall", artist: "Oasis"),
Song(name: "Rain", artist: "이적")
]
var moveCount = 0
var songCount = 0
for item in library {
if item is Movie {
moveCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(moveCount) move and \(songCount) songs")
as 연산자 사용
// 위 for문을 아래와 같이 변경
for item in library {
if let move = item as? Movie {
print("Move: \(movie.name), dir. \(movie.director)"
} else if let song = item as? Song {
print("Song: \(song.name), by \(movie.artist)"
}
}
19. assert 와 guard
assert
- 특정 조건을 체크하고, 조건이 성립되지 않으면 메세지를 출력하게 할 수 있는 함수
- assert 함수는 디버깅 모드에서만 동작하고 주로 디버깅 중 조건의 검증을 위하여 사용합니다.
guard
- 뭔가를 검사하여 그 다음에 오는 코드를 실행할지 말지 결정하는 것
- guard문에 주어진 조건문이 거짓일 때 구문이 실행됨
assert
실행시 아래와 같은 에러 발생
__lldb_expr_3/MyPlayground.playground:11: Assertion failed: 값이 0이 아닙니다.
Playground execution failed:
error: Execution was interrupted, reason: EXC_BREAKPOINT (code=1, subcode=0x18f2e9a10).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x18f2e9a10)
* frame #0: 0x000000018f2e9a10 libswiftCore.dylib`Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 300
frame #1: 0x0000000100d18534 $__lldb_expr4`main at MyPlayground.playground:0
frame #2: 0x000000010097f4f0 MyPlayground`linkResources + 264
frame #3: 0x000000018034ff90 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
frame #4: 0x000000018034f254 CoreFoundation`__CFRunLoopDoBlocks + 408
frame #5: 0x0000000180349b10 CoreFoundation`__CFRunLoopRun + 764
frame #6: 0x0000000180349308 CoreFoundation`CFRunLoopRunSpecific + 572
frame #7: 0x000000018bf025ec GraphicsServices`GSEventRunModal + 160
frame #8: 0x0000000184d3b4e0 UIKitCore`-[UIApplication _run] + 992
frame #9: 0x0000000184d3ffc0 UIKitCore`UIApplicationMain + 112
frame #10: 0x000000010097f5b0 MyPlayground`main + 192
frame #11: 0x0000000100b8dc04 dyld_sim`start_sim + 20
frame #12: 0x0000000100d2d0f4 dyld`start + 520
guard 문
// 1 값 비교
func guardTest(value: Int) {
guard value = 0 else { return }
print("안녕하세요.")
}
}
guardtest(value: 2)
// 2 옵셔널 바인딩
func guardTest(vnale: Int?) {
guard let value = value else { return }
print(value)
}
guardTest(value: 2)
guardTest(value: nil)
20. 프로토콜
특정 역활을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진
protocol SomeProtocol {
}
protocol SomeProtocol2 {
}
struct SomeStructure: SomeProtocol, SomeProtocol2 {
}
protocol FirstProtocol {
var name: Int { get set }
var age: Int { get } // 읽기전용
}
protocol AnotoerProtocol {
static var someTypeProperty: Int { get set }
}
protocol FullyNames {
var fullName: String { get set }
func printFullName()
}
struct Person: FullyNames {
var fullName: String
func printFullName() {
print(fullName)
}
}
protocol SomeProtocol3 {
func someTypeMethod()
}
protocol SomeProtocol4 {
init(someParameter: Int)
}
protocol SomeProtocol5 {
init()
}
class Someclass: SomeProtocol5 {
required init() { // 클래스에서는 required 식별자 필요
}
}
21. Extention
기존의 클래스, 구조체, 열거형, 프로토콜에 새로운 기능을 추가하는 기능
익스텐션이 타입에 추가할 수 있는 기능
- 연산타입 프로퍼티 / 연산 인스턴트 프로퍼티
- 타입 메서드 / 인스턴트 메서드
- 이니셜라이저
- 서브스크립트
- 중첩 타입
- 특정 프로토콜을 준수할 수 있도록 기능 추가
extension Int {
var isEven: Bool {
return self % 2 == 0
}
var isOdd: Bool {
return self % 2 == 1
}
}
var number = 3
number.isEven
number.isOdd
extension String {
func convertToInt() -> Int? {
return Int(self)
}
}
var string = "0"
string.convertToInt()
22. 열거형
연관성 있는 값을 모아 놓은 것을 말한다.
enum CompassPoint: String {
case north = "북"
case south = "남"
case east = "동"
case west = "서"
}
var direction = CompassPoint.east
direction = .west
switch direction {
case .north:
print(direction.rawValue)
case .south:
print(direction.rawValue)
case .east:
print(direction.rawValue)
case .west:
print(direction.rawValue)
}
let diretion2 = CompassPoint.init(rawValue: "남")
enum PhoneError {
case unknown
case batteryLow(String)
}
let error = PhoneError.batteryLow("베터리가 곧 방전 됩니다.")
switch error {
case .batteryLow(let message):
print(message)
case .unknown:
print("알 수 없는 에러입니다. ")
}
23. 옵셔널 체이닝
옵셔널에 속해 있는 nil 일지도 모르는 프로퍼티, 메서드, 서브스크립션 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정
struct Developer {
let name: String
}
struct Company {
let name: String
var developer: Developer?
}
var developer = Developer(name: "han")
var company = Company(name: "Gunter", developer: developer)
print(company.developer)
print(company.developer?.name)
print(company.developer!.name)
24. try-catch
에러처리란?
프로그램 내에서 에러가 발생한 상황에 대해 대응하고 이를 복구하는 과정
Swift 에러 처리
- 발생(throwing)
- 감지(catching)
- 전파(propagating)
- 조작(manipulating)
enum PhoneError: Error {
case unkonwn
case batteryLow(batteryLevel: Int)
}
throw PhoneError.batteryLow(betteryLevel: 20) // 직접 던지기
/*
Playground execution terminated: An error was thrown and was not caught:
▿ PhoneError
▿ batteryLow : 1 element
- betteryLevel : 20
*/
func checkPhoneBatteryStatus(batteryLevel: Int) throws -> String {
guard batteryLevel != -1 else { throw PhoneError.unkonwn }
guard batteryLevel > 20 else { throw PhoneError.batteryLow(batteryLevel: 20) }
return "베터리 상태가 정상입니다. "
}
// 첫번째 방법
do {
try checkPhoneBatteryStatus(batteryLevel: 20)
} catch PhoneError.unkonwn {
print("알수 없는 에러입니다.")
} catch PhoneError.batteryLow(let batteryLevel) {
print("베터리 전원 부족 남은 베터리: \(batteryLevel)")
} catch {
print("그 외 오류 발생: \(error)")
}
// 두번째 방법
let status = try? checkPhoneBatteryStatus(batteryLevel: -1)
print(status)
let status2 = try? checkPhoneBatteryStatus(batteryLevel: 30)
print(status2)
25. 클로져
코드에서 전달 및 사용할 수 있는 독립 기능 블록이며, 일급 객체의 역할을 할 수 있음
일급객체 : 전달인자로 보낼 수 있고, 변수/상수 등으로 저장하거나 전달할 수 있으며, 함수의 반환 값이 될 수도 있다.
NameClosure, Unnamed Closure
클로져 표현식
let hello = { () -> () in
print("hello")
}
hello()
let hello2 = { (name: String) -> String in
return "Hello, \(name)"
}
//hello2(name: "Gunter") // X: 에러발생
hello2("Gunter") // O: 전달인자의 값을 적지 않고 보내야함
func doSomething(closer: ()-> ()) {
closer()
}
doSomething() {
print("hello2")
}
doSomething {
print("hello5")
}
doSomething(closer: { () -> () in
print("hello")
})
func doSomething2() -> () -> () {
return { () -> () in
print("hello4")
}
}
doSomething2()()
func doSomething2(success: () -> (), fail: () -> ()) {
}
doSomething2 {
<#code#>
} fail: {
<#code#>
}
// 클로져 간소화
func doSomething3(closure: (Int, Int, Int) -> Int) {
closure(1, 2, 3)
}
doSomething3(closure: { (a, b, c) in
return a+b+c
})
doSomething3(closure: {
return $0+$1+$2 // 약식인수 이름으로 대체
})
doSomething3(closure: {
$0+$1+$2
})
doSomething3() {
$0+$1+$2
}
doSomething3 {
$0+$1+$2
}
26. 고차함수
다른 함수를 전달 인자로 받거나 함수 실행의 결과로 함수로 반환하는 함수
스위프트에서 제공하는 고차함수
- map
- filter
- reduce
// map
let number = [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]
// 컨네이너 내부 요소를 하나로 통합
let someArray = [1, 2, 3, 4, 5]
let reduceResult = someArray.reduce(0) { // 0 : 초기값
(result: Int, element: Int) -> Int in
print("\(result) + \(element)")
return result + element
}
print("reduce \(reduceResult)") // reduce 15