-
반응형
회사의 사정으로 인해 갑자기 iOS의 업무 일부를 맡게 되었다.
그나마 다행인건 Kotlin과 Swift가 유사한 언어라는 점에서 빠르게 학습할 수 있었고,
Swift를 공부할 때 어떤 부분이 Kotlin과 유사한 문법인지를 찾아보면서 이해를 더 쉽게 할 수 있었다.
이제 XCode IDE에 익숙해지고 RXSwift같이 회사 프로젝트에 적용되어 있는 기술들도 찾아볼 예정
아래 내용은 공부하면서 노션에 적어두긴 했지만, 블로그에도 같이 작성해두려 한다.
var 변경 가능한 변수
let 변경 불가능한 상수
기본 데이터 타입
var someInt: Int = -100 // 양, 음의 정수
var someUInt: UInt = 100 // 양의 정수
var someFloat: Float = 3.14
var someDouble: Double = 3.14
var someCharacter: Character = "a" // 한 글자
var someString: String = "하하하"
var someAny: Any = 100 // 모든 타입
var someAnyObject: AnyObject = SomeClass() // 모든 클래스 타입을 지칭하는 프로토콜
nil // 데이터 없음
함수
- 반환 값이 있는 함수
func sum(a: Int, b: Int) -> Int {return a + b}sum(a: 3, b: 5)- 반환 값이 없는 함수
func printMyName(name: String) -> Void {print(name)}printMyName(name: "동환")func printMyName(name: String = "동환") { // 매개변수 기본값 설정print(name)}- 매개변수가 없는 함수
func maximumIntegerValue() -> Int {return Int.max}- 매개변수와 반환값이 없는 함수
func printLog() {print("Test")}- 전달인자
- swift에는 함수의 파라미터로 함수 외부에서 입력하는 전달인자 라는 개념이 있다. 아래 예시로 함수를 사용할 때는 to, from을 사용하고, 함수 내부에서는 friend, me 사용
func greeting(to friend: String, from me: String) {print("Hello \\(friend)! I'm\\(me)")}greeting(to: "친구", from: "동환")- 가변 매개변수
- 전달 받을 값의 개수를 알기 어려울 때 사용 가변 매개변수는 함수당 하나만 가질 수 있다
func sayHelloToFriends(me: String, friends: String...) -> String {return "Hello \\(friends)! I'm \\(me)"}sayHelloToFriends(me: "동환", friends: "친구1", "친구2", "친구3")- 함수의 타입 표현
func greeting(to friend: String, from me: String) {print("Hello \\(friend)! I'm\\(me)")}// String, String 타입의 greeting 함수를 리턴하는 someFunction 변수var someFunction: (String, String) -> Void = greeting(to:from:)someFunction("친구", "동환")조건문
if (someInteger < 100) {print("100 미만")} else if someInteger > 100 { // if문 괄호 없어도 됨print("100 초과")} else {print("100")}let someInteger: Int = 100switch someInteger {case 0:print("0")case 1..<100:print("1~99")case 100:print("100")case 101...Int.max:print("over 100")default: // 예외 상황이 있을 때는 default가 반드시 있어야 함.print("unknown")}반복문
var integers = [1, 2, 3]let people = ["yagom": 10, "eric": 15, "mike": 12]// for 문for num in integers {print(num)}// Dictionary for 문 사용for (name, age) in people {print("\\(name): \\(age)")}// white 문 (괄호 있어도, 없어도 됨)while integers.count > 1 {integers.removeLast()}// 다른 언어의 do white과 동일// do를 사용안한 이유는 swift는 오류 처리에서 do를 사용하기 때문repeat {integers.removeLast()} while integers.count > 0옵셔널
- 옵셔널은 값이 있을 수도 있고, 없을 수도 있음.
- nil의 가능성을 명시적으로 표현
- 코틀린과 마찬가지로 자료형에 ? 사용
func someFunction(someOptionalParam: Int?) {}someFunction(someOptionalParam: nil)func someFunction(someParam: Int) {}- ? : nil 일수도 있음
- ! : 값이 반드시 있음. nil이면 에러 발생
구조체
Swift의 구조체(Struct)는 데이터와 기능을 캡슐화하여 하나의 사용자 정의 데이터 타입을 정의할 수 있는 강력한 도구입니다. Swift의 구조체는 값 타입(Value Type)이며, 클래스와 유사한 기능을 많이 제공합니다. 구조체는 프로그램의 데이터를 모델링하고 기능을 포함하는 데 자주 사용됩니다.
- 값 타입(Value Type)
- 구조체는 값 타입으로, 변수나 상수에 할당되거나 함수에 전달될 때 복사됩니다.
- 값 타입은 각 복사본이 독립적으로 존재하며, 하나의 인스턴스를 변경해도 다른 복사본에는 영향을 주지 않습니다.
- 속성과 메서드 포함 가능
- 구조체는 저장 속성, 계산 속성, 메서드 등을 가질 수 있습니다.
- 클래스와 유사한 방식으로 동작하지만 상속은 지원하지 않습니다.
- 초기화 메서드 제공
- Swift는 구조체에 대해 자동으로 초기화 메서드(Initializer)를 생성합니다.
- 프로토콜 준수 가능
- 구조체는 프로토콜을 채택하고 요구 사항을 구현할 수 있습니다.
- 상속 불가
- 구조체는 다른 구조체 또는 클래스로부터 상속할 수 없습니다. 클래스만 상속을 지원합니다.
1. 저장 속성
구조체 내부에 데이터를 저장할 수 있다.
struct Point {var x: Doublevar y: Double}let point = Point(x: 3.0, y: 4.0)print(point.x) // 3.02. 계산 속성
계산된 값을 반환하는 속성으로, 저장되지 않고 호출될 때마다 계산된다.
struct Rectangle {var width: Doublevar height: Doublevar area: Double {return width * height}}let rect = Rectangle(width: 5.0, height: 4.0)print(rect.area) // 20.03. 메서드
구조체는 인스턴스 메서드와 타입 메서드를 가질 수 있다.
struct Circle {var radius: Doublefunc circumference() -> Double {return 2 * Double.pi * radius}}let circle = Circle(radius: 5.0)print(circle.circumference()) // 31.415926535897934. 가변 메서드
값 타입에서 메서드 내의 속성을 수정하려면 mutating 키워드를 사용해야 한다.
struct Counter {var count = 0mutating func increment() {count += 1}}var counter = Counter()counter.increment()print(counter.count) // 15. 타입 속성과 타입 메서드
static 키워드를 사용하여 타입 수준에서 동작하는 속성과 메서드를 정의할 수 있다.
struct Math {static let pi = 3.141592653589793static func square(_ value: Double) -> Double {return value * value}}print(Math.pi) // 3.141592653589793print(Math.square(3)) // 9.0언제 구조체를 사용해야 할까?
• 독립적인 값 복사가 필요한 경우: 값이 변경될 때 다른 복사본에 영향을 주지 않아야 하는 경우.
• 상속이 필요하지 않은 경우: 상속이나 다형성이 필요하지 않다면 구조체를 사용.
• 간단한 데이터 모델링: 좌표(Point), 크기(Size), 범위(Range) 등.
• 경량 데이터 처리: 메모리 효율적으로 데이터 처리.
클래스
구조체는 값 타입, 클래스는 참조 타입이라는 것이 크게 다른 점이다.
스위프트의 클래스는 다중 상속이 되지 않는다.
클래스도 구조체와 유사하게 프로퍼티와 메서드를 가질 수 있다.
- 참조 타입(Reference Type)
- 클래스의 인스턴스는 변수에 할당되거나 함수로 전달될 때 참조(Reference)가 복사됩니다.
- 같은 인스턴스를 여러 곳에서 참조하면, 한 곳에서 값을 변경할 경우 모든 참조에 영향을 미칩니다.
- 상속 가능(Inheritance)
- 클래스는 다른 클래스로부터 속성과 메서드를 상속할 수 있습니다.
- 이는 코드 재사용성을 높이는 데 유용합니다.
- 초기화 메서드 제공
- 클래스는 초기화 메서드(init)를 정의하여 초기 상태를 설정할 수 있습니다.
- 필요에 따라 디이니셜라이저(deinit)를 사용하여 리소스를 해제할 수 있습니다.
- 타입 캐스팅(Type Casting):
- 클래스의 인스턴스는 런타임에서 다른 타입으로 캐스팅할 수 있습니다.
- 값 비교:
- 클래스는 참조 비교를 사용하여 동일성(identity)을 확인합니다.
- === 연산자를 사용하여 두 참조가 같은 인스턴스를 가리키는지 확인합니다.
- 프로토콜 준수 가능:
- 클래스는 프로토콜을 채택하고 요구 사항을 구현할 수 있습니다.
초기화와 디이니셜라이저
- init : 클래스의 인스턴스를 생성할 때 호출되는 초기화 메서드
- deinit : 인스턴스가 메모리에서 해제될 때 호출되는 디이니셜라이저
class Person {var name: Stringinit(name: String) {self.name = nameprint("\\(name) is initialized")}deinit {print("\\(name) is being deinitialized")}}var person: Person? = Person(name: "Alice")person = nil // "Alice is being deinitialized"열거형
enum은 타입이므로 대문자 카멜 케이스를 사용하여 이름 정의
각 case들은 소문자 카멜 케이스 사용
enum Weekday {case mon // 하나씩 케이스를 만들어도 되고case thucase wedcase thu, fri, sat, sun // 하나의 케이스에 여러개를 쓸 수도 있다}var day: Weekday = Weekday.mon // 이렇게 사용열거형은 switch 구문과 함께 자주 사용된다.
func navigate(to direction: Direction) {switch direction {case .north:print("Move North")case .south:print("Move South")case .east:print("Move East")case .west:print("Move West")}}navigate(to: .north) // "Move North"각 케이스에 기본값(문자열, 정수 등)을 할당할 수 있다.
enum Weekday: String {case monday = "Mon"case tuesday = "Tue"case wednesday = "Wed"case thursday = "Thu"case friday = "Fri"}print(Weekday.monday.rawValue) // "Mon"열거형의 원시 값이 Int인 경우, 첫번째 값 이후 자동으로 증가한다.
enum Rank: Int {case one = 1case twocase three}print(Rank.two.rawValue) // 2열거형에 메서드를 정의할 수 있다.
enum TrafficLight {case red, yellow, greenfunc description() -> String {switch self {case .red:return "Stop"case .yellow:return "Caution"case .green:return "Go"}}}let light = TrafficLight.greenprint(light.description()) // "Go"값 타입과 참조 타입
값 타입
- 데이터를 전달할 때 값을 복사하여 전달
참조 타입
- 데이터를 전달할 때 값의 메모리 위치를 전달
값 타입 예제
struct Point {var x: Intvar y: Int}var point1 = Point(x: 10, y: 20)var point2 = point1 // 독립적인 복사본 생성point2.x = 30print(point1.x) // 10 (point1은 변경되지 않음)print(point2.x) // 30참조 타입 예제
class Point {var x: Intvar y: Intinit(x: Int, y: Int) {self.x = xself.y = y}}let point1 = Point(x: 10, y: 20)let point2 = point1 // 동일한 참조point2.x = 30print(point1.x) // 30 (point1과 point2는 동일한 참조를 공유)print(point2.x) // 30- 스위프트는 구조체, 열거형 사용을 선호
- Apple 프레임워크는 대부분 클래스 사용
- Apple 프레임워크 사용시 구조체/클래스 선택은 우리의 몫
클로저
클로저(Closure) 는 Swift에서 중괄호 {}로 정의되는 코드 블록을 의미하며, 익명 함수와 유사한 개념으로 사용됩니다. 클로저는 데이터를 캡슐화하거나 특정 작업을 수행하는 코드를 작성하는 데 유용하며, 변수나 상수처럼 다른 곳으로 전달하거나 저장할 수 있습니다.
기본 문법
{ (parameters) -> returnType in// 실행 코드}예시
let greet = { (name: String) -> String inreturn "Hello, \\(name)!"}print(greet("Alice")) // "Hello, Alice!"클로저 표현식 간소화
- 매개변수와 반환값 타입 추론
let greet = { name in"Hello, \\(name)!"}print(greet("Bob")) // "Hello, Bob!"
- 타입을 생략하면 Swift가 타입을 추론한다.
- 매개변수 이름 축약
let square = { $0 * $0 }print(square(5)) // 25
- $0, $1 같은 축약 매개변수 이름을 사용할 수 있다.
- 후행 클로저
let numbers = [1, 2, 3, 4]let doubled = numbers.map { $0 * 2 }print(doubled) // [2, 4, 6, 8]
- 클로저가 함수의 마지막 매개변수로 전달될 때, 소괄호 ( ) 밖에 작성할 수 있다.
비동기 작업
클로저는 비동기 작업 (예 : 네트워크 요청, 타이머) 에서 콜백으로 자주 사용된다.
func fetchData(completion: @escaping (String) -> Void) {DispatchQueue.global().async {// 비동기 작업 수행let data = "Sample Data"DispatchQueue.main.async {completion(data)}}}fetchData { data inprint("Fetched data: \\(data)")}UI 이벤트 처리
Swift UI 및 UIKit에서 버튼 클릭이나 제스처 인식을 처리할 때 클로저를 사용한다.
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)@objc func buttonTapped() {print("Button was tapped!")}프로퍼티
인스턴스 저장 프로퍼티
var name: String = ""var 'class': String = "Swift"var koreanAge: Int = 0인스턴스 연산 프로퍼티
var westernAge: Int {get {return koreanAge - 1}set(inputValue) {koreanAge = inputValue + 1}}타입 저장 프로퍼티
static var typeDescription: String = "학생"
읽기 전용 인스턴스 연산 프로퍼티
- get만 있으면 읽기 전용, 쓰기 전용은 없다.
var selfIntroduction: String {get {return "저는 \\(self.class)반 \\(name)입니다"}}set에서 특별히 (inputValue)와 같이 선언하지 않으면 기본적으로 newValue 를 사용하면 된다.
struct Money {var currencyRate: Double = 1480var dollar: Double = 0var won: Double {get {return dollar * currencyRate}set {dollar = newValue / currencyRate}}}var moneyInMyPoket = Money()moneyInMyPoket.won = 14800 // 14800moneyInMyPoket.dollar = 10 // 14800프로퍼티 감시자
프로퍼티 감시자는 willSet과 didSet이라는 두 가지 키워드를 사용하여 정의된다.
var propertyName: Type {willSet(newValue) {// 값이 설정되기 직전에 호출}didSet {// 값이 설정된 직후에 호출}}willSet
- 값이 변경되기 직전에 호출됩니다.
- newValue라는 매개변수로 새 값을 전달받습니다.
- newValue 이름을 생략하면 기본적으로 newValue가 사용됩니다.
didSet
- 값이 변경된 직후에 호출됩니다.
- oldValue라는 매개변수로 이전 값을 참조할 수 있습니다.
- oldValue 이름을 생략하면 기본적으로 oldValue가 사용됩니다.
기본 예제
struct Counter {var count: Int = 0 {willSet(newCount) {print("Count will be set to \\(newCount)")}didSet {print("Count was set from \\(oldValue) to \\(count)")}}}var counter = Counter()counter.count = 10// 출력:// "Count will be set to 10"// "Count was set from 0 to 10"유효성 검사
값을 설정할 때 유효성을 확인하거나, 특정 조건을 만족하지 않으면 값을 되돌리는 로직
var age: Int = 0 {didSet {if age < 0 {print("Invalid age, resetting to 0")age = 0}}}age = -5// "Invalid age, resetting to 0"print(age) // 0UI 업데이트
UI가 프로퍼티 값에 따라 동적으로 변경되어야 할 때 사용할 수 있다.
var score: Int = 0 {didSet {print("Updating UI: New score is \\(score)")}}score = 100// 출력:// "Updating UI: New score is 100"의존 값 자동 업데이트
한 프로퍼티가 다른 프로퍼티에 의존할 경우, 값을 자동으로 업데이트 할 수 있다.
var length: Double = 0.0 {didSet {area = length * width}}var width: Double = 0.0 {didSet {area = length * width}}var area: Double = 0.0length = 10.0width = 5.0print(area) // 50.0상속
- 클래스, 프로토콜 등에서 상속이 가능하다
- 열거형, 구조체는 상속 불가능
- 스위프트는 다중상속을 지원하지 않는다
class Person {var name: String = "동환"func selfIntroduce() {print("저는 \\(name)입니다")}// final 키워드를 사용하여 재정의를 방지할 수 있습니다final func sayHello() {print("hello")}// 재정의 불가 타입 메서드static func typeMethod() {}// 재정의 가능 타입 메서드class func classMethod() {}// 메서드 앖의 final class는 static과 똑같은 역할이므로 재정의 할 수 없다final class func finalClassMethod() {}}인스턴스의 생성과 소멸
스위프트에서 클래스 내부의 프로퍼티는 기본값이 반드시 있어야 한다.
만약 프로퍼티 기본값을 지정하기 어려운 경우에는 이니셜라이저를 통해 인스턴스가 가져야 할 초기값을 전달할 수 있다.
class Person {var name: Stringvar age: Intvar nickName: Stringinit(name: String, age: Int, nickName: String) {self.name = nameself.age = ageself.nickName}}만약 프로퍼티의 초기 값이 꼭 필요 없다면 null 허용을 하면 된다
var nickName: String?
암시적 추출 옵셔널 (kotlin : lateinit)
인스턴스 사용에 꼭 필요하지만 초기값을 할당하지 않고자 할 때 사용
class Puppy {var name: Stringvar owner: Person!init(name: String) {self.name = name}}반드시 있어야 하는 owner에 ! 를 붙여서 반드시 필요함을 알려주고, 나중에 초기화 한다.
kotlin의 lateinit과 비슷한 것 같다.
옵셔널 체이닝과 nil 병합 연산자
코틀린과 유사해서 너무 좋다!
var guardJob: StringguardJob = donghwan?.home?.guard?.job ?? "슈퍼맨"? 로 nil 인지 아닌지 확인하는 것이 옵셔널 체이닝
?? 로 nil 이면 “슈퍼맨” 을 적용하는 것이 nil 병합 연산자
타입 캐스팅
인스턴스의 타입을 확인하는 용도.
is, as 를 사용한다.
키워드 역할
is 객체가 특정 타입인지 확인. 결과는 true 또는 false. as 캐스팅을 수행. 성공해야만 동작하며, 실패 시 런타임 에러 발생 as? 옵셔널 캐스팅. 캐스팅 성공 시 옵셔널 타입으로 반환, 실패 시 nil. as! 강제 캐스팅. 캐스팅 실패 시 런타임 에러 발생. 타입 확인 (is)
class Animal {}class Dog: Animal {}class Cat: Animal {}let pet: Animal = Dog()if pet is Dog {print("This is a Dog")} else if pet is Cat {print("This is a Cat")}// 출력: "This is a Dog"안전한 캐스팅 (as?)
let pet: Animal = Dog()if let dog = pet as? Dog {print("This is a Dog: \\(dog)")} else {print("Not a Dog")}// 출력: "This is a Dog: Dog"강제 캐스팅 (as!)
let pet: Animal = Dog()let dog = pet as! Dog // 강제 캐스팅print("This is a Dog: \\(dog)")// 출력: "This is a Dog: Dog"assert
- assert 함수는 디버깅 모드에서만 동작한다.
- 배포하는 앱에서는 제외된다.
- 주로 디버깅 중 조건의 검증을 위해 사용한다.
간단한 조건 검사
let age = 25assert(age >= 0, "Age cannot be negative")- 위 코드에서 age가 음수일 경우 프로그램이 중단되고, 메시지 "Age cannot be negative"가 출력된다.
논리적 오류 확인
let number = 10assert(number % 2 == 0, "Number must be even")// 조건이 참이므로 프로그램 계속 실행배열 인덱스 검사
let array = [1, 2, 3]let index = 2assert(index >= 0 && index < array.count, "Index out of bounds")// index가 유효한지 확인print(array[index]) // 정상 실행guard
- guard를 사용하여 잘못된 값을 전달시 특정 실행 구문을 빠르게 종료한다.
- 디버깅 모드 뿐만 아니라 어떤 조건에서도 동작한다.
- guard의 else 블럭 내부에는 특정 코드 블럭을 종료하는 지시어 (return, break 등)가 있어야 한다.
- 타입 캐스팅, 옵셔널과도 자주 사용된다.
1. 기본 사용
func checkNumber(_ number: Int) {guard number > 0 else {print("Number must be greater than 0")return}print("Number is \\(number)")}checkNumber(5) // 출력: "Number is 5"checkNumber(-3) // 출력: "Number must be greater than 0"2. 옵셔널 바인딩
guard let을 사용하여 옵셔널 값을 안전하게 언래핑할 수 있습니다.
func greet(name: String?) {guard let unwrappedName = name else {print("Name is nil")return}print("Hello, \\(unwrappedName)!")}greet(name: "Alice") // 출력: "Hello, Alice!"greet(name: nil) // 출력: "Name is nil"3. 여러 조건 검사
func validateUser(age: Int?, name: String?) {guard let age = age, age >= 18, let name = name, !name.isEmpty else {print("Invalid user")return}print("Welcome, \\(name), age \\(age)")}validateUser(age: 20, name: "Alice") // 출력: "Welcome, Alice, age 20"validateUser(age: nil, name: "Bob") // 출력: "Invalid user"validateUser(age: 16, name: "Eve") // 출력: "Invalid user"프로토콜
코틀린의 인터페이스와 비슷하다!!
어려운 설명
Swift에서 특정 역할을 수행하기 위해 필요한 메서드, 프로퍼티, 또는 기타 요구사항을 정의하는 청사진(blueprint)입니다. 클래스, 구조체, 열거형은 프로토콜을 채택(Adopt)하고, 정의된 요구사항을 구현(Implement)해야 합니다.
쉬운 설명
이 기능이 꼭 필요해! 이 기능을 꼭 만들어 둬야해 라고 강요하는 것.
어떤 객체가 이런 기능을 제공해야 한다 라는 약속을 정하는 것.
기본 문법
protocol ProtocolName {// 메서드, 프로퍼티 요구사항 정의}프로토콜 정의
protocol Greetable {var name: String { get } // 읽기 가능한 프로퍼티func greet() // 메서드 요구사항}프로토콜 채택 및 구현
struct Person: Greetable {var name: Stringfunc greet() {print("Hello, my name is \\(name).")}}let person = Person(name: "Alice")person.greet() // 출력: "Hello, my name is Alice."코틀린과 비교
Swift
protocol Greetable {var name: String { get }func greet()}struct Person: Greetable {var name: Stringfunc greet() {print("Hello, \\(name)!")}}Kotlin
interface Greetable {val name: Stringfun greet()}class Person(override val name: String) : Greetable {override fun greet() {println("Hello, $name!")}}익스텐션
코드 분리와 재사용성 향상
익스텐션을 사용하면 관련 기능을 한 곳에 모아 코드의 모듈화를 도울 수 있습니다.
→ 코틀린의 확장 함수와 유사함
계산 프로퍼티 추가
익스텐션 타입으로 기존 타입에 새로운 계산 프로퍼티를 추가할 수 있다.
extension Int {var squared: Int {return self * self}}let number = 5print(number.squared) // 출력: 25메서드 추가
extension String {func isPalindrome() -> Bool {return self == String(self.reversed())}}let word = "racecar"print(word.isPalindrome()) // 출력: true오류 처리
사용자 정의 에러를 만들 수 있다.
Error 타입은 열거형으로 구현되는 경우가 많고, 에러를 정의하고 throw, catch를 통해 에러를 처리한다.
1. 에러 정의
enum NetworkError: Error {case badURLcase timeoutcase noInternetConnection}2. 에러 발생 (throw)
func fetchData(from url: String) throws {guard url.starts(with: "http") else {throw NetworkError.badURL}// 데이터 요청 로직}3. 에러 처리 (do-catch)
do {try fetchData(from: "invalid-url")} catch NetworkError.badURL {print("Invalid URL.")} catch NetworkError.timeout {print("Request timed out.")} catch {print("An unknown error occurred: \\(error).")}
에러 처리 키워드
throws
- 함수나 메서드가 에러를 발생시킬 수 있음을 나타낸다.
- throws는 함수 선언에 붙으며, 반환 타입 전에 작성된다.
func riskyFunction() throws -> String {throw NetworkError.timeout}try
- 에러를 발생시킬 가능성이 있는 함수 호출 앞에 작성한다.
do {try riskyFunction()} catch {print("An error occurred.")}try?
- 에러를 옵셔널 값 nil로 처리한다. 에러가 발생하면 nil이 반환된다.
let result = try? riskyFunction() // 에러 발생 시 result는 nil
try!
- 에러가 발생하지 않을 것이라고 확신할 때 사용해야 한다.
- 에러가 발생하면 런타임 오류가 발생한다.
let result = try! riskyFunction() // 에러 발생 시 앱이 크래시
고차 함수
- 전달 인자로 함수를 전달받거나, 함수 실행의 결과를 함수로 반환하는 함수.
- ex ) map, filter, reduce
- 다른 함수를 매개변수로 받거나, 함수를 반환하거나, 둘 다 수행할 수 있는 함수를 말합니다. Swift는 고차 함수를 통해 더 간결하고 읽기 쉬운 코드를 작성할 수 있도록 다양한 함수형 프로그래밍 기능을 제공합니다.
map
컬렉션의 각 요소를 변환하여 새 배열을 한봔한다.
let numbers = [1, 2, 3, 4, 5]let squares = numbers.map { $0 * $0 } // 각 요소를 제곱print(squares) // [1, 4, 9, 16, 25]filter
filter 조건을 만족하는 요소만 포함하는 새 배열을 반환한다.
let numbers = [1, 2, 3, 4, 5]let evenNumbers = numbers.filter { $0 % 2 == 0 } // 짝수만 포함print(evenNumbers) // [2, 4]reduce
초기값과 연산을 사용해 모든 요소를 결합하여 단일 값을 만든다.
let numbers = [1, 2, 3, 4, 5]let sum = numbers.reduce(0) { $0 + $1 } // 합계 계산print(sum) // 15forEach
let names = ["Alice", "Bob", "Charlie"]names.forEach { print($0) }// 출력:// Alice// Bob// CharliecompactMap
옵셔널 요소를 제거하고, 비-옵셔널 값으로 이루어진 배열을 반환한다.
let strings = ["1", "two", "3", "four"]let numbers = strings.compactMap { Int($0) } // 문자열을 정수로 변환, 실패한 경우 제거print(numbers) // [1, 3]flatMap
중첩된 컬렉션을 평평한 단일 컬렉션으로 변환한다.
let nestedArray = [[1, 2, 3], [4, 5], [6]]let flatArray = nestedArray.flatMap { $0 }print(flatArray) // [1, 2, 3, 4, 5, 6]sorted
컬렉션의 요소를 정렬된 새로운 배열로 반환
let numbers = [5, 2, 3, 1, 4]let sortedNumbers = numbers.sorted() // 기본 오름차순 정렬print(sortedNumbers) // [1, 2, 3, 4, 5]let descending = numbers.sorted { $0 > $1 } // 내림차순 정렬print(descending) // [5, 4, 3, 2, 1]고차 함수의 결합
let numbers = [1, 2, 3, 4, 5]let result = numbers.filter { $0 % 2 == 0 } // 짝수만 필터링.map { $0 * $0 } // 제곱 계산print(result) // [4, 16]반응형