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
복사