Search

Swift - Basic

본 글에서는 iOS Application 개발의 가장 기초가 되는 Swift Language에 대해 다룹니다.

1. 변수(var)

값(데이터)를 메모리에 저장하는 공간, 메모리 할당 후 변할 수 있는 값
var greeting: String = "Hello, Playground." var age: Int = 26 age = 27 age = 28 ...
Swift
복사

2. 상수(let)

값(데이터)를 메모리에 저장하는 공간, 메모리 할당 후 변하지 않는 값
let name: String = "John" // 변하지 않음
Swift
복사

3. Type Annotation

변수 혹은 상수의 타입을 프로그래머가 직접 지정해줌으로써 Compile Time을 단축 시킬 수 있습니다.
Swift에서는 Character와 String 모두 “” 를 사용하여 값을 초기화 하지만, 별도의 Type Annotation이 없을 경우 Default는 String이기 때문에, 하나의 문자(Character)만 사용할 경우에는 Type Annotation을 해주어야 합니다.
let screentHeight: Float = 560 print(type(of: screenHeight)) let character: Character = "A" let string = "A" print(type(of: character), type(of: string))
Swift
복사

4. Tuple

하나의 쌍을 이루는 값
var httpError = (statusCode: 404, value: "Page Not Found") httpError.statusCode httpError.value var title = ("메인화면", "mainIcon.png") title.0 // 메인화면 title.1 // mainIcon.png
Swift
복사

5. 옵셔널(Optional)

옵셔널은 값이 있을 수도 있고, 없을 수도 있는 것을 의미합니다.
Swift에서는 값이 없는 것을 null 대신 nil을 사용합니다.
보다 안전하게 코드를 작성할 수 있습니다.
// 값이 있을 수도 있고, 없을 수도 있다. // 0은 값이 없는 것이 아닌, 다 쓴 값으로 생각 // 값이 없는 것은 nil var myAge: Int? if myAge == nil { // Do Something. }
Swift
복사

6. 옵셔널 추출(Optional Unwrapping)

Swift에서 Optional은 enum 타입으로 Wrapping 되어 있기 때문에 값을 사용하기 위해서 Unwrapping(추출) 하는 과정이 필요합니다.
옵셔널 바인딩, 강제 추출 등이 있는데 강제 추출의 경우 값이 있다는 확신이 있을 때만 사용하는 것이 좋습니다.(값이 없는데 강제로 추출할 경우, Runtime Error가 발생합니다.)
// 1. Coalesce - ?? var a: Int? = 10 var b: Int? = 20 var c = (a ?? 0) + (b ?? 0) // 2. Force Unwrapping - ! // Runtime Error가 발생할 수 있기 때문에, 값이 있다는 확신이 있을 때 사용하는 것을 권고 var d = a! + b! // 3. Optional Binding // if Statements // if let // if var // guard let // guard var if let hansNumber = a { // Do Something. } if var hasNumber = a { // Do Something. } func functionA() -> Void { guard let hasNumber = a else { return } } func functionB() -> Void { guard var hasNumber = a else { return } }
Swift
복사

7. 기본 연산자(Basic Operator)

Swift의 기본 사칙연산과 여러 연산자에 대해 다룹니다.
Swift에서는 별도의 증감 연산자(++, — 등)가 없습니다. 사용하고 싶은 경우 사용자 정의 연산자(Custom Operator)로 직접 구현해서 사용해야 합니다.
// +, -, *, /, % var someNumber: Int = 20 someNumber += 2 someNumber -= 2 someNumber *= 2 someNumber /= 2 someNumber %= 2 // Swift는 Type Safe한 Programming Language let number1: Int = 20 let number2: Int = 30 let number3 = number1 / number2 // Integer let number4: Double = 20 let number5: Double = 30 let number6 = number4 / number5 // Double let number7: Int = 20 let number8: Double = 30 // let sum = number7 + number8 // Error let sum = number7 + Int(number8) let str1: String = "Hi" let str2: String = " Hello" let str3: String = str1 + str2 // Comparison Operator number1 == number2 number1 != number2 number1 > number2 number1 < number2 number1 >= number2 number1 <= number2 if number1 > number2 { // Do Something. } else { // Do Something. }
Swift
복사

8. Unicode

Unicode를 통해, 기본 문자, 특수 문자 등을 사용할 수 있습니다.
// 숫자만 입력 받아야 하는 경우 let inputValue = "19" if inputValue >= "\u{30}" && inputValue <= "\u{39}" { print("숫자") } else { print("숫자가 아님") }
Swift
복사

9. 문자열(String)

Swift의 String에 대해 다룹니다.
Swift는 타입에 엄격한(Type Safety)한 언어이지만, “\()”과 같이 문자열 보간법(String Interpolation)으로 다른 타입을 문자열 타입으로 편리하게 Casting할 수 있습니다.
let myName: String = "lee" for character in myName { // Do Something. } let myLongStr: String = """ hi hello welcome """ let isOn: Bool = true print(isOn.description) // "true\n" let myNumber: Int = 123 print(String(myNumber)) // "123\n" print("My number is \(myNumber)") // "My number is 123\n"
Swift
복사

10. Collection Type - 배열(Array)

Swift의 Collection Type 중, 동일한 타입의 값을 메모리상에 연속적으로 저장하는 Array(배열)에 대해 다룹니다.
배열은 index기반으로 메모리 주소에 접근하기 때문에, 전체를 탐색하는 경우 O(N), 특정 index를 탐색하는 경우 O(1)이라는 시간 복잡도로 데이터 탐색이 빠르다는 장점을 가지고 있습니다.
반면에, 데이터를 삽입, 삭제 하는 경우 메모리 상에 연속적으로 존재하기 때문에, 값을 뒤로 미루거나 앞으로 땡기는 작업을 필요로 하기 때문에 하나의 데이터를 삽입 혹은 삭제할 때 마다, 최악의 경우 O(N)이라는 시간복잡도를 가지게 되므로, 다소 비효율적이라고 말할 수 있겠습니다.
var myNames: Array<String> = Array() var myAges: [Int] = [] myNames.append("kim") myNames.append("lee") myNames.append("jin") // myNames[3] // Index out of range // 방어적 코드 // 안전한 코딩 스타일 let index: Int = 2 if myNames.count > index { print(myNames[index]) } myNames.append(contentsOf: ["hi", "hello"]) if myNames.count == 0 { // Do Something. } if myNames.isEmpty { // Do Something. } myNames.insert("hahaha", at: 2) for name in myNames { print(name + "님") } for (index, name) in myNames.enumerated() { print(index, name) }
Swift
복사

11. Collection Type - Set

Swift의 Collection Type 중, Set(집합)에 대해 다룹니다.
Array와 유사하지만, 차이점이라고 하면, 순서라는 개념이 없습니다.
또, 중복된 값은 추가되지 않으며 집합이기 때문에, 교집합, 합집합, 차집합, 여집합 등과 같은 연산을 할 수 있습니다.
// Array와 달리 순서가 없음 var names: Set<String> = Set() names.insert("kim") names.insert("min") // 중복된 값은 추가 되지 않음 names.insert("lee") names.insert("lee") names.insert("lee") var names2: Set = ["lee", "kim", "min"] var numbers1: Set = [1, 2, 3, 4, 5] var numbers2: Set = [4, 5, 6, 7, 8] // 교집합 numbers1.intersection(numbers2) // 합집합 numbers1.union(numbers2) // 합집합 - 교집합 numbers1.symmetricDifference(numbers2) // 여집합 numbers1.subtracting(numbers2)
Swift
복사

12. Collection Type - Dictionary

Swift의 Collection Type 중, Dictionary에 대해 다룹니다.
이 또한 순서적인 개념이 없으며, Key - Value의 형태를 이룹니다.
// 순서적인 개념이 없음 // Key-Value 형태 var namesOfStreet: Dictionary<String, Any> = Dictionary() namesOfStreet["302ro"] = "1st Street" namesOfStreet["303ro"] = "2nd Street" namesOfStreet["304ro"] = 3 namesOfStreet.keys for (key, value) in namesOfStreet { print(key, value) }
Swift
복사

13. 흐름 제어(Control Flow)

조건문, 반복문과 같은 흐름 제어(Control Flow)에 대해 다룹니다.
let name: String = "anna" for char in name { // Do Something. } let numberOfLegs = ["ant" : 6, "dog" : 4] for dic in numberOfLegs { // Do Something. } for index in 0...5 { // Do Something. } var some: Int = 0 while some < 10 { print("계속 실행") some += 1 } // Switch // 조건에 케이스를 만들어서 분기 let someAlpha: String = "b" switch someAlpha { case "b": // Do Something. default: // Do Something. }
Swift
복사

14. 함수(Function)

어떠한 연산 과정에 대한 결과를 반환하는 함수에 대해 다룹니다.
func simpleFunc() -> Void { print("Simple Func") } func plus(num1: Int, num2: Int) -> Int { return num1 + num2 } func minus(_ num1: Int, _ num2: Int) -> Int { return num1 - num2 } func multiply(_ num1: Int, _ num2: Int) -> Int { return num1 * num2 } // 반환 타입이 없는 경우(Void)는 생략 가능 func calc(result: (Int, Int) -> Int) { print("연산 결과", result(10, 20)) } var inputButtonType = "+" if inputButtonType == "+" { calc(result: plus) } else if inputButtonType == "-" { calc(result: minus) } else if inputButtonType == "*" { calc(result: multiply) }
Swift
복사

15. 클로저(Closure)

Swift의 클로저는 코드의 블록이라고도 하며, 이름이 없고 함수와 유사합니다.
이름 있는 클로저를 함수라고 말할 수 도 있습니다.
고차 함수, 비동기 처리와 같은 작업 등에 사용할 수 있겠습니다.
다양한 축약 문법이 존재하므로, 과도한 축약보다는 적절한 축약 문법을 사용하는 것이 좋겠습니다.
// Closure - 코드의 블럭이라고도 하며, 이름이 없고 함수와 유사 // Function Version func myScore(a: Int) -> String { return "\(a)점" } // Closure Version let myScore2: (Int) -> String = { (a: Int) -> String in return "\(a)점" } // Closure 축약 // 1. return 생략 - body가 한 줄일 경우 let myScore3: (Int) -> String = { (a: Int) -> String in "\(a)점" } // 2. 반환 값을 추론하여 반환 타입 생략 let myScore4: (Int) -> String = { (a: Int) in "\(a)점" } // 3. 파라미터 타입 생략 let myScore5: (Int) -> String = { a in "\(a)점" } // 4. 단축 인자 이름, in 키워드 생략 let myScore6: (Int) -> String = { "\($0)점" } // Closure 실전 let someNames = ["apple", "air", "brown", "red", "orange", "blue", "candy", "hobby"] let isContainsSomeText: (String, String) -> Bool = { name, find in if name.contains(find) { return true } return false } let isStartSomeText: (String, String) -> Bool = { name, find in if name.first?.description == find { return true } return false } // Function Version func findByFunction(find: String) -> [String] { var newNames: [String] = [] for name in someNames { if name.contains(find) { newNames.append(name) } } return newNames } // Closure Version func findByClosure(findString: String, _ condition: (String, String) -> Bool) -> [String] { var newNames: [String] = [] for name in someNames { if condition(name, findString) { newNames.append(name) } } return newNames } findByFunction(find: "a") findByClosure(findString: "a", isContainsSomeText) findByClosure(findString: "a", isStartSomeText) let result = findByClosure(findString: "y") { $0.last?.description == $1 ? true : false } print(result) var someArr = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] someArr.sort { $0 < $1 } someArr.sort(by: { $0 < $1 } ) someArr.sort(by: < )
Swift
복사

16. 열거형(Enumeration, enum)

Swift의 enum에 대해 다룹니다.
유사한 타입들을 열거할 때 주로 사용하며, 타입 분류를 더 쉽게 하기 위해 사용합니다.
Value Type이기 때문에 전달인자로 전달 될 때 값이 복사돼서 전달 됩니다.(Call By Value)
// Enum // Value Type(값 타입) - 값이 복사돼서 전달됨, 타입 분류를 더 쉽게 하기 위해 사용 enum BookType { case fiction(title: String, price: Int, year: Int), comics(title: String, price: Int, year: Int), workbook(title: String, price: Int, year: Int) } var bookStyle: BookType? var books: [BookType] = [] func saveBook(book: BookType) { books.append(book) } saveBook(book: .fiction(title: "aaa", price: 5000, year: 2020)) saveBook(book: .comics(title: "bbb", price: 6000, year: 2021)) saveBook(book: .comics(title: "ccc", price: 7000, year: 2010)) saveBook(book: .workbook(title: "ddd", price: 7000, year: 2010)) saveBook(book: .fiction(title: "eee", price: 4000, year: 202)) saveBook(book: .fiction(title: "fff", price: 8000, year: 2015)) for book in books { if case let BookType.workbook(_, _, year) = book { print(year) } switch book { case let .comics(_, price, _): print(price) case let .fiction(title, _, _): print(title) default: break } }
Swift
복사

17. 클래스(Class)

Swift의 class에 대해 다룹니다.
iOS Framework는 주로 class로 이루어져 있습니다.
Enum, Struct은 상속이 불가능 하지만, Class는 상속이 가능하며, 단일 상속만 가능합니다.
또, Reference Type이기 때문에 전달인자로 전달 될 때 참조가 전달 됩니다.(Call By Reference)
// Class // Reference Type(참조 타입) - 참조가 전달됨 class MyInfo { enum GenderType { case male case female } var genderType: GenderType? var name: String = "" var age: Int = 0 func isAdult() -> Bool { return self.age > 19 } init(gender: GenderType) { self.genderType = gender } } // 참조 예시 var myInfo: MyInfo = MyInfo(gender: .female) myInfo.age = 20 var myInfo2: MyInfo = myInfo print(myInfo2.age) myInfo2.age = 30 print(myInfo.age) print(myInfo2.age)
Swift
복사

18. 상속(Class - Inheritance)

Swift Class의 상속에 대해 다룹니다.
부모 클래스의 기능을 물려받아 사용하기도 하며, Override해서 사용하기도 합니다.
이는 코드의 재사용성을 늘리고 중복을 줄일 수 있습니다.
단, 단일상속만 가능합니다.
// 단일 상속 class SportsInfo { var homeScore: Int = 0 var awayScore: Int = 0 func presentScore() -> String { return homeScore.description + " : " + awayScore.description } } class Soccer: SportsInfo { var time: Int = 0 override func presentScore() -> String { return "\(super.presentScore()), \(time)" } } class Baseball: SportsInfo { var round: Int = 30 override func presentScore() -> String { return "\(super.presentScore()), \(round)" } } class Football: SportsInfo { } let soccer: Soccer = Soccer() soccer.time = 45 soccer.homeScore = 1 soccer.presentScore() // "1 : 0, 45" let baseball: Baseball = Baseball() baseball.presentScore() // "0 : 0, 30" let football: Football = Football() football.homeScore // 0
Swift
복사

19. 프로퍼티(Property)

Class, Struct, Enum 등에 정의된 변수 혹은 상수를 말합니다.
저장 프로퍼티, 연산 프로퍼티, 타입 프로퍼티(static) 등이 존재합니다.
Image와 같은 트래픽을 많이 요구하는 프로퍼티의 경우 lazy 키워드를 통해 사용할 때 메모리에 적재 되도록 지정할 수 있습니다.
class MyInfoClass { // Stored Property(저장 프로퍼티) var name: String = "" var age: Int = 0 // Lazy Stored Property(Class가 메모리에 적재될 때 같이 되는 것이 아니고, 사용하려고 할 때 메모리에 load됨.) lazy var myProfiles: [UIImage?] = [UIImage(named: "n") ?? nil, UIImage(named: "a") ?? nil] // Computed Property(연산 프로퍼티) var isAdult: Bool { return age > 19 } private var _email: String = "" var email: String { get { return _email } set { _email = newValue.hash.description } } } let myInfoClass: MyInfoClass = MyInfoClass() myInfoClass.age = 27 myInfoClass.name = "lee" myInfoClass.email = "lyJ@vaultmicro.com" myInfoClass.myProfiles print(myInfoClass.isAdult) print(myInfoClass.email)
Swift
복사

20. 생성자(Initializer, init)

Swift의 Type의 인스턴스 생성 시, 프로퍼티에 대한 값을 초기화 하는 역할을 합니다.
class SomeClass { var name: String var id: String init(name: String, id: String) { self.name = name self.id = id } // convenience initializer, 다른 init을 반드시 실행해야 함 convenience init() { self.init(name: "", id: "") } } var someClass: SomeClass = SomeClass(name: "lee", id: "abcd")
Swift
복사

21. 메모리 해제(Deinitialization, deinit)

Swift의 메모리 관리 기법은 ARC(Automatic Refernce Counting)를 사용합니다.
이는 Class 인스턴스가 강한 참조를 받게 되는 경우 ARC Count 값이 1 증가 하게 됩니다.
ARC Count 값이 1 이상인 경우에는 메모리에서 해당 인스턴스가 해제되지 않습니다.
ARC Count 값이 0 일 경우에 메모리에서 해제되게 됩니다.
주의할 점은, Class 타입의 인스턴스 끼리 강한참조 하게 될경우 강한 참조 순환 문제가 발생하며,
메모리 누수(Memory Leak)가 발생하게 됩니다.
따라서 이러한 경우 생명주기가 더 짧은 Class 타입에서 weak 키워드를 통해 참조하게 되면 ARC Count 값이 증가하지 않아 메모리 누수(Memory Leak)를 방지할 수 있겠습니다.
또, Class 타입 인스턴스가 메모리에서 해제 될 때 deinit 함수가 실행되게 됩니다.
var someInteger: Int? = 10 a = nil class Game { var score: Int = 0 var name: String = "" var round: Round? init() { print("game init") } deinit { print("game deinit") } } class Round { weak var gameInfo: Game? //메모리 누수(Memory Leak) 방지 var lastRound: Int = 10 var roundTime: Int = 20 deinit { print("round deinit") } } var game: Game? = Game() var round: Round? = Round() round?.gameInfo = game game?.round = round game = nil round = nil
Swift
복사

22. 구조체(Structure, struct)

Swift의 struct에 대해 다룹니다.
Swift의 구조는 주로 struct으로 이루어져 있습니다.
struct의 경우 상속이 불가능하며, Value Type이기 때문에 전달인자로 전달될 때 값이 복사돼서 전달 됩니다.(Call By Value)
// Struct - 구조체 // Value Type(값 타입) - 값이 복사돼서 전달됨 struct SomeStruct { var name: String = "" } var someStruct1: SomeStruct = SomeStruct() var someStruct2: SomeStruct = someStruct1 someStruct1.name = "lee" someStruct2.name = "kim" print(someStruct1) print(someStruct2)
Swift
복사

23. Extension

Swift의 Extension은 struct, class, enum, protocol과 같은 Type의 기능을 확장합니다.
이미 정의되어 있는 기능은 extension을 통해 재정의는 할 수 없습니다.
주로, delegate 패턴과 함께 extension을 사용할 수 있겠습니다.
// struct, class, enum, protocol 과 같은 // type들의 기능을 확장함, 이미 정의되어 있는 기능은 extension을 통해 재정의할 수 없음 extension Int { var oddOrEven: String { return self % 2 == 0 ? "짝수" : "홀수" } } 3.oddOrEven 4.oddOrEven extension UIColor { var mainColor1: UIColor { UIColor(red: 50/255, green: 70/255, blue: 120/255, alpha: 1) } } var button: UIButton = UIButton() button.titleLabel?.textColor = UIColor().mainColor1
Swift
복사

24. 프로토콜(Protocol)

Swift는 객체지향, 함수형, 프로토콜지향 프로그래밍이라고 할 수 있습니다.
프로토콜은 네트워크에서도 많이 사용되는 용어로, 규격, 규약, 규칙이라고 말할 수 있겠습니다.
struct, enum, class 등이 프로토콜을 채택한다고 말하며, 구현했으면 하는 기능의 명세서라고도 말할 수 있겠습니다.
상속과 달리 여러 프로토콜을 채택할 수 있지만, SOLID 설계 원칙 중 인터페이스 분리 원칙(Interface segregation principle)에 의해 필요한 프로토콜만 채택해서 구현하는 것이 좋겠습니다.
단, Class의 경우, “:(콜론)” 뒤에 가장 처음으로 오는 것이 상속 이거나 프로토콜 채택일 수 있고, 그 뒤에는 모두 프로토콜 채택이라고 할 수 있겠습니다.
// 규격, 규약, 규칙 protocol UserInfo { var name: String { get set } var age: Int { get set } func isAdult() -> Bool } // 프로토콜 지향 프로그래밍(Protocol Oriented Programming, POP) extension UserInfo { func isAdult() -> Bool { return self.age > 19 } } // protocol 채택 class Guest: UserInfo { var name: String = "kim" var age: Int = 20 } class Member: UserInfo { var name: String var age: Int init(name: String, age: Int) { self.name = name self.age = age } } class VIPMember: UserInfo { var name: String = "jane" var age: Int = 10 } class UserInfoPresenter { func present() { let guest: Guest = Guest() let member: Member = Member(name: "lee", age: 27) let vip: VIPMember = VIPMember() let members: [UserInfo] = [guest, member, vip] for element in members { print(element.name) } } } let presenter: UserInfoPresenter = UserInfoPresenter() presenter.present()
Swift
복사

25. 제네릭(Generic)

Swift의 Generic에 대해 다룹니다.
한 번의 정의를 통해, 여러가지 타입을 사용하고 싶을 때 Generic을 사용하면 좋습니다.
다만, Generic이 수용할 수 있는 타입에 대해 제한을 둘 수 있는데, 이 때 where 키워드를 사용합니다.
// Generic <Type> // 타입을 여러가지 사용할 때 사용 // 타입에 제한을 둘 때 제네릭 명 옆에 where 키워드 사용 struct Stack<SomeType> { var items: [SomeType] = [] mutating func push(item: SomeType) { self.items.append(item) } mutating func pop() -> SomeType? { if items.isEmpty { return nil } return items.removeLast() } } var myStack: Stack<Int> = Stack() myStack.push(item: 4) myStack.push(item: 5) myStack.push(item: 6) myStack.pop() myStack.pop() myStack.pop() myStack.pop() var myStack2: Stack<String> = Stack() myStack2.push(item: "4") myStack2.push(item: "5") myStack2.push(item: "6") myStack2.pop() myStack2.pop() myStack2.pop() myStack2.pop()
Swift
복사

26. 고차 함수(Higher Order Function)

Swift의 Higher Order Function(고차 함수)에 대해 다룹니다.
고차 함수는 함수를 반환 하거나, 전달인자로 함수를 전달 받는 함수를 말합니다.
고차 함수를 사용하면, 코드를 편리하고 간결하게 작성할 수 있는 장점이 있습니다.
// 고차함수를 사용하면 코드를 편리하고 간결하게 작성할 수 있는 장점이 있음 // 1. Map let nameArr: [String] = ["kim", "lee", "min", "john"] let nameArrMap = nameArr.map { $0 + " 님" } // ["kim 님", "lee 님", "min 님", "john 님"] let nameArrSMap2 = nameArr.map { name in name.count // [3, 3, 3, 4] } // 2. Filter let filterNames = nameArr.filter { name -> Bool in name.count > 3 // ["john"] } // 3. Reduce let reduceNames = nameArr.reduce("") { $0 + $1 } // "kimleeminjohn" let numberArr = [1, 2, 3, 4, 5, nil, 6, nil, 8] let sumNum = numberArr.reduce(0) { $0 + ($1 ?? 0) } // 29 // 4. CompactMap - nil을 제외함 let compactMap = numberArr.compactMap { $0 } // [1, 2, 3, 4, 5, 6, 8] // 5. FlatMap let numberArr2 = [[1, 2, 3], [4, 5, 6]] let flatNum = numberArr2.flatMap { $0 } // [1, 2, 3, 4, 5, 6]
Swift
복사