Swift 기초 문법 심화

클로저

{ (parameter) -> return type in
	// 구현 코드	
}
// 함수와 클로저 비교
func pay(user: String, amount: Int) {
	// code
}
let payment = { (user: String, amount: Int) in
	// code
}
func func1(_ param: String) -> String {
	return param + "!"
 }

func func2(name: String) -> String {
	return name + "***"
}

var a: (String) -> String = func1

// 함수를 변수에 할당가능(변수가 함수를 가르키게 됨)
var a: (String) -> String = func1 // func1의 타입을 가지는 변수를 선언함으로써 변수 a에 함수 func1을 할당한 것

a("안녕")

a = func2

a("hello")

// 함수(클로저)를 변수에 할당해서
let closure1 = { (param: String) -> String in         // 클로저 리터럴
    return param + "!"
}

// 사용(실행)
closure1("스티브")

⭐️ 클로저 문법 최적화 과정

closure(param: { (str: String) in return str.count } )

// 1. 타입 추론
closure(param: { str in return str.count } )

// 2. closure가 한 줄일 경우, return을 생략할 수 있음
closure(param: { str in str.count } )

// 3. Argument 이름을 축약할 수 있음(첫 번째 Argument = $0, 두 번째 Argument = $1 ...)
closure(param { $0.count } )

// 4. Trailing Closure 후행 클로저(매개변수 생략 가능)
closure() { $0.count }

// 5. 함수 생성자 생략
closure { $0.count }

예시) 함수의 입력타입과 반환타입이 (Int, Int) -> Int 일 때

let closure: (Int, Int) -> Int = { (a, b) in return a * b }

// Argument 이름 축약까지 진행하면
let closure: (Int, Int) -> Int = { $0 * $1 }

// (a, b) in return a * b 부분이 $0 * $1 구문으로 축약되었다고 보면 된다.

탈출 클로저

클로저가 메소드의 인자로 전달되었을 때, 메소드의 실행이 종료된 후 실행되는 클로저(비동기)
파라미터 타입 앞에 무조건 @escaping 키워드를 붙여주어야 함

또한, @escaping 키워드가 붙은 클로저가 self의 요소를 사용할 때 명시적으로 self.x 등과 같이 언급해주어야 함

비동기적으로 실행되는 함수

DispatchQueue.global().async {
	let result = "finished"
    print(result)
}
/* 
위의 함수는 어떤 함수들의 앞에 있어도 순차적으로 실행되는 것이 아니라,
비동기적으로, 다른 스레드에서 실행되어지게 됨.
따라서 함수의 실행은 스레드에서 작업이 끝나는 순간 진행되어짐.
즉, 같은 파일에 있는 함수들과 병행으로 실행된다는 의미
*/

고차함수

map 함수 : 컬렉션 내부의 기존 데이터를 변형하여 새로운 컬렉션을 생성

예시 )

let numbers = ["1", "2", "3", "4", "5"]
var numArray: [Int] = number.map{ Int($0) }
print(numArray) // [1, 2, 3, 4 ,5]

filter 함수 : 기존 컨테이너의 요소 중 조건에 만족하는 값으로 이루어진 새로운 컨테이너를 만들어서 반환

예시)

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var oddNumArray: [int] = numbers.filter{ $0 % 2 == 0 }
print(oddNumArray) // [2, 4, 6, 8, 10]

reduce 함수 : 기존 컨테이너 요소에 대해 정의한 클로저로 매핑한 결과를 새로운 컨테이너로 반환

예시)

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var sum: Int = numbers.reduce(0, +) // 초기값 0, 모든 요소를 더한 값을 반환
var sum2 : Int = numbers.reduce(0){ $0 + $1 } // 위의 코드와 같은 역할을 함

예외처리

Error : 던져질 수 있는 오류 값을 나타내는 유형, Error프로토콜을 채택하여 사용자 정의 에러를 정의할 수 있음

throwsdo-catch

throws : 리턴값 반환 이전에 오류 발생 시에 에러 객체를 반환한다는 의미. 오류가 발생할 가능성이 있는 메소드 제목 옆에 사용
throw : 오류가 발생할 구간에서 사용
do-catch : throw로 던진 에러를 do-catch 문에서 처리한다.

예시)

enum CustomError: Error {
    case outOfBounds
    case invalidInput(String)
}

func processValue(_ value: Int) throws -> Int {
    if value < 0 {
        throw CustomError.invalidInput("Value cannot be negative")
    } else if value > 100 {
        throw CustomError.outOfBounds
    }
    
    return value * 2
}

// do-catch 블록을 이용하여 throwing 함수 호출 및 에러 처리하기
do {
    let result = try processValue(50)
    print("Result is \(result)")
} catch CustomError.outOfBounds {
    print("Value is out of bounds!")
} catch CustomError.invalidInput(let errorMessage) {
    print("Invalid Input: \(errorMessage)")
} catch {
    print("An error occurred: \(error)")
}
// 출력 : Result is 100


do {
    let result = try processValue(-10)
    print("Result is \(result)")
} catch CustomError.outOfBounds {
    print("Value is out of bounds!")
} catch CustomError.invalidInput(let errorMessage) {
    print("Invalid Input: \(errorMessage)")
} catch {
    print("An error occurred: \(error)")
}
// 출력 : Invalid Input: Value cannot be negative

try, try?, try!

  1. try : 에러가 발생할 수 있는 코드 블록 표시, 에러를 던질 수 있는 함수나 메소드 호출 시 사용됨. do-catch문을 사용해서 에러를 처리할 수 있음.
  2. try? : do-catch 문 없이 사용가능, 에러 발생 시에는 nil값을 반환, 에러가 없다면 옵셔널 값을 반환함.
  3. try! : 에러 발생 시 프로그램 강제 종료, 반환 타입은 언제나 옵셔널이 해제된 값, 오류가 없다는 보장이 있을 때 사용하는 것을 권장.
enum MyError: Error {
    case invalidInput
}

func someThrowingFunction(value: Int) throws -> String {
    guard value >= 0 else {
        throw MyError.invalidInput // value가 음수인 경우 에러를 던짐
    }

    return "The value is \(value)"
}

// throwing 함수 호출과 에러 처리하기
do {
    let result = try someThrowingFunction(value: 5)
    print(result)
} catch {
    print("Error occurred: \(error)")
}

do {
    let result = try someThrowingFunction(value: -2) // 에러 발생
    print(result)
} catch {
    print("Error occurred: \(error)") // 음수 값을 처리하는 에러
}

// try?를 사용하여 에러 처리하기
let result1 = try? someThrowingFunction(value: 5) // 유효한 값 호출
print(result1) // Optional("The value is 5")

let result2 = try? someThrowingFunction(value: -2) // 에러 발생
print(result2) // nil

// try!를 사용하여 에러 처리하기
let result3 = try! someThrowingFunction(value: 5) // 유효한 값 호출
print(result3) // The value is 5

let result4 = try! someThrowingFunction(value: -2) // 에러 발생
print(result4)

ARC와 메모리 누수

ARC (Automatic Reference Counting) : 객체의 참조 횟수를 자동으로 카운팅하여 객체의 생명주기를 결정하는 기능.

객체가 생성될 때마다 참조 횟수가 1 증가, 해당 객체를 참조하는 다른 객체나 변수가 없어지거나 더 이상 사용되지 않을 때 참조 횟수가 1 감소한다. 참조 횟수가 0이 되면 해당 객체는 메모리에서 해제.

  • 작동 방식
    • 객체 생성 : 객체 생성 시 참조 횟수가 1 증가
    • 객체 참조 : 다른 변수나 상수에 객체를 할당할 시에 참조 횟수가 1 증가
    • 참조 해제 : 객체의 참조가 없어지면(변수나 상수가 없어지거나, nil이 할당되는 행위) 참조 횟수 1 감소
    • Zeroing Weak References : 약한 참조(Weak References)의 객체가 메모리에서 해제될 때 nil로 설정하여 메모리 crush를 예방하는 기능.

강한 참조 순환(Strong Reference Cycle) : 메모리가 해제되지 않고 유지되어 메모리가 누수되는 현상

강한 참조 순환은 메모리 누수 문제를 일으키는 문제
참조는 기본적으로 (default 값으로) 강한 참조를 사용함. 이 때, 참조를 잘못 사용하면 메모리 누수 문제가 발생할 가능성이 있음.

대표적인 메모리 누수가 일어나는 경우는 두 개 이상의 인스턴스가 서로를 강한 참조할 때 발생됨.

문제 해결 방법

  • 약한 참조(Weak Reference) : 참조되는 대상을 약하게 참조하여 순환 참조를 방지
    • weak 키워드를 사용하는 참조 사용, 옵셔널로 선언되는 참조, 참조 대상이 메모리에서 해제되면 자동으로 nil로 설정됨.
    • weak 키워드가 붙으면 다른 객체에서 해당 부분을 참조해도 참조횟수를 올리지 않음.
  • 비소유 참조(Unowned Reference) : 참조되는 대상이 항상 유효한 경우에만 사용, 해당 대상이 해제될 수 있는 상황에는 사용하지 않음.
    • 비소유 참조는 항상 값이 있다고 가정하며, 함조되는 객체가 해제되면 런타임 에러 발생 가능
    • unowned 참조는 참조 대상이 해제될 수 있는 경우에만 사용. 그리고 그 객체가 메모리에서 해제되지 않은 상태에서만 해당 unowned 참조를 사용해야 함

프로토콜

특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등을 정의해놓은 규약 혹은 약속

프로토콜

  • 클래스, 구조체, 열거형이 프로토콜을 채택하고 모든 요구사항을 충족하면 그제서야 프로토콜을 준수했다고 표현함.
  • 설계된 조건만 정의하고 제시할 뿐, 스스로 기능을 구현하지 않음.
  • 프로토콜에서는 이름과 기능, gettable, settable을 명시함.
    • gettable : 추 후에 값을 할당할 수 있는지
    • settable : 미리 할당된 값을 읽기만 할 수 있게됨.
  • 항상 var 변수로 선언해야 함.
  • 메소드를 정의할 때 메소드 이름과 리턴값을 지정할 수 있고, 구현 코드는 적지 않음.
  • 프로토콜은 다중 상속이 가능
// 예시
protocol Student {
    var studentId: Int { get set }
    var name: String { get }
    func printInfo() -> String
}

struct UnderGraduateStudent: Student {
    var studentId: Int
    var name: String
    var major: String
    
    func printInfo() -> String {
        return "\(name), whose student id is \(studentId), is major in \(major)"
    }
}

struct GraduateStudent: Student {
    var studentId: Int
    var name: String
    var degree: String
    var labNumber: Int
    
    func printInfo() -> String {
        return "\(name), member of lab no.\(labNumber), has a \(degree) degree"
    }
}

// 프로토콜은 타입으로서도 사용가능
let underGraduate: Student = UnderGraduateStudent(studentId: 1, name: "홍길동", major: "computer")
let graduate: Student = GraduateStudent(studentId: 2, name: "김철수", degree: "master", labNumber: 104)

let studentArray: [Student] = [underGraduate, graduate]

프로토콜은 타입으로써 사용할 수도 있다는 점을 명심하자!

associatedtype : 프로토콜 내에서 실제 타입을 명시하지 않고, 해당 프로토콜을 채택하는 타입에서 실제 타입을 결정하도록 하는데 사용

  • associatedtype으로 정의된 타입은, 프로토콜을 채택한 클래스나 구초제, 열거형 내에서 그 타입을 정의할 수 있음을 의미함

typealias : 기존 타입에 대해 새로운 이름을 지정하거나, 복잡한 타입에 대한 간결한 별칭을 생성할 때 사용


확장 Extension

Extension

  • structure, class, enum, protocol 타입에 새로운 기능 추가 가능(수평 확장의 개념)
  • 구현 가능한 것들
    • 새로운 계산된 속성 추가
    • 새로운 인스턴스/타입 메소드 추가
    • 새로운 초기화 추기
    • 프로토콜 채택
    • 서브 스크립트 추가
    • 중첩타입 추가
extension <TypeName> {
	code blocks // 추가하고 싶은 속성, 인스턴스, 초기화 구문, 프로토콜, 서브스크립트, 중첩타입 추가
}
  • 구현 불가능한 것들
    • 저장 프로퍼티 추가 불가능
    • 기존 기능의 재정의 불가능
    • 초기화 메서드의 재정의 불가능
    • 기존 타입에 저장된 프로퍼티의 기본값 설정 불가능

제네릭 Generic

함수, 타입, 데이터 구조에 대한 유연하고 추상적인 코드를 작성할 수 있게 해주는 기능

Generic

  • 제네릭으로 구현한 기능과 타입은 재사용도 쉽고, 코드의 중복도 줄일 수 있음.
  • placeholder ex) T, U, V etc. 사용(타입의 종류를 알려주지 않지만, 어떤 타입이라는 것을 의미)
  • placeholder <T, U>와 같이 여러개 구현 가능

inout 키워드 : 매개변수에 붙여, 함수 외부에서도 호출이 가능하게끔 해주는 키워드. 호출 시 값 앞에 '&' 문자를 붙여 해당 값을 참조로 호출 가능

// 함수 정의
func increment(_ value: inout Int) {
    value += 1
}

var number = 5
print("Before increment: \(number)") // 출력: Before increment: 5

// 함수 호출 시 매개변수에 &를 사용하여 변수의 참조를 전달
increment(&number)

print("After increment: \(number)") // 출력: After increment: 6

// 제네릭을 사용하면 타입에 상관없이 사용가능, a, b는 T로 표현된 똑같은 타입을 가져야 함
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

where 키워드 : 제네릭의 제약조건(Constraints) 중 하나. 해당 제약을 충족하는 타입만을 사용하도록 강제함. 특정 프로토콜 채택, 특정 타입과의 상속 관계 등을 제한 가능.

// 프로토콜 채택 제약 예시
func process<T>(value: T) where T: Numeric {
    // Numeric 프로토콜을 채택하는 타입만을 제네릭 타입 T로 받음
    print("Value is a numeric type.")
}

process(value: 5) // 출력: Value is a numeric type.
process(value: 3.14) // 출력: Value is a numeric type.
// process(value: "Hello") // 컴파일 에러 - 문자열은 Numeric 프로토콜을 채택하지 않음


// 클래스의 상속 관계 제약 예시
class MyClass {}
class MySubclass: MyClass {}

func process<T>(value: T) where T: MyClass {
    print("Value is an instance of MyClass or its subclasses.")
}

let obj = MySubclass()
process(value: obj) // 출력: Value is an instance of MyClass or its subclasses.
// process(value: "Hello") // 컴파일 에러 - 문자열은 MyClass 또는 그 하위 클래스가 아님

비동기와 네트워킹

스레드 Thread : 컴퓨터 프로세스 내에서 실행되는 실행 흐름의 단위

  • 메인 스레드 : 앱의 주요 인터페이스 및 UI업데이트를 담당하는 스레드
  • 백그라운드 스레드 : 메인 스레드 이외에 동시에 작업을 처리하기 위해 생성되는 스레드

동기와 비동기

  • 동기 sync : 작업이 순차적으로 실행되는 것을 의미
  • 비동기 async : 작업이 별도의 스레드 또는 백그라운드에서 실행, 다른 작업을 기다리지 않고 동시에 실행. 작업 완료 시 콜백 callback 혹은 완료 핸들러 completion handler를 통해 결과를 처리

직렬과 동시

  • 직렬 serial : 분산처리시킨 작업을 한 스레드에서 처리하는 것
  • 동시 concurrent : 시스템이 정한 여러 개의 스레드로 분산 처리하는 것

GCD Grand Central Dispatch

  • Apple 플랫폼에서 멀티스레드 프로그래밍을 간단하고 효율적으로 구현하기 위한 기술
  • 특징
    • Queue 기반의 작업 관리 : 작업 세분화 및 실행할 큐에 따라 작업의 속성 결정 가능
    • 간편한 비동기 작업 처리 : 비동기적으로 작업을 수행할 수 있도록 함. 프로그래머가 별도의 스레드를 관리할 필요가 없음
    • 큐의 종류에 따른 다양한 동작 방식 제공 : 직렬 큐 serial queue, 동시 큐 concurrent queue 운영
    • 콜백을 통한 작업 완료 처리
    • QoS Quality of Service 지원 : 각 큐에 대한 우선순위 설정 지원, 성능 최적화가 용이함
  • DispatchQueue.main
    • 주로 앱의 UI 업데이트와 관련된 작업을 처리하는데 사용
    • 메인 스레드에서 동작, 주로 사용자 UI 업데이트하거나 상호작용을 처리할 때 사용
    • 위에서 설명한 작업들과 같이 메인 스레드에서 실행해야 한다면 DispatchQueue.main을 통해 실행
    • 메인 스레드는 사용자의 경험에 직접적으로 개입하기 때문에, 주의해서 사용하지 않으면(최적화를 생각하지 않고 무분별하게 사용하면) 사용자의 경험이 부정적으로 변할 가능성이 있음.(UI 응답성 저하 등)
  • DispatchQueue.global()
    • DispatchQueue.global()은 전역 스레드에서 작업을 처리하는데 사용
    • 여러 스레드에서 동시에 실행되는 비동기 작업을 처리할 때 유용함
    • 전역 DispatchQueue에서 async, sync 를 사용하여 작업을 추가할 시, 백그라운드에서 실행됨
// 동기적으로 실행되는 작업
DispatchQueue.global().sync {
    print("Synchronous Task")
}

// 비동기적으로 실행되는 작업
DispatchQueue.global().async {
    print("Asynchronous Task")
}

// 백그라운드에서 비동기 작업 실행
DispatchQueue.global().async {
    // 여기서 백그라운드에서 실행될 작업을 수행합니다.
    for i in 1...5 {
        print("Background Task \(i)")
    }
    
    // 작업이 완료되었음을 메인 스레드로 알립니다.
    DispatchQueue.main.async {
        print("Background Task Completed, Updating UI")
        // UI 업데이트 등을 수행할 수 있습니다.
    }
}

네트워킹 : 외부 서버 또는 인터넷 리소스와 데이터를 주고받는 작업

  • 주로 URLSession을 사용하여 수행됨. 비동기적으로 처리하는 것이 일반적임
  • URLSession으로 네트워크 요청을 보내면 기본적으로 비동기 방식으로 처리됨

// URLSession의 dataTask 함수 정의
func dataTask(
    with url: URL,
    completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask // 비동기 함수이기 때문에 @escaping 키워드 작성


// 네트워크 작업을 처리할 함수
func fetchData() {
    if let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") {
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            // 네트워크 작업 완료 후 실행될 코드 (비동기적으로 실행)
            if let error = error {
                print("Error: \(error.localizedDescription)")
                return
            }
            
            if let httpResponse = response as? HTTPURLResponse {
                print("Status code: \(httpResponse.statusCode)")
            }
            
            if let data = data {
                do {
                    // JSON 데이터 파싱
                    let json = try JSONSerialization.jsonObject(with: data, options: [])
                    if let jsonDict = json as? [String: Any] {
                        // 파싱된 데이터 활용
                        print("Received JSON data: \(jsonDict)")
                    }
                } catch {
                    print("JSON parsing error: \(error.localizedDescription)")
                }
            }
        }
        
        task.resume() // 네트워크 작업 시작 (비동기적으로 실행됨)
    }
}

// fetchData 함수 호출
fetchData()

Combine

함수형 반응형 프로그래밍을 위한 프레임워크, 비동기적인 데이터 스트림을 다루고 조작하는데 사용

  • 반응형 프로그래밍 : 데이터 스트림 또는 데이터의 변화에 따라 코드가 자동으로 반응하는 프로그래밍 패러다임. 데이터 변경 사항을 감지하고 연속적으로 반응하는 방식으로 프로그램을 작성함.

데이터 스트림

  • 이벤트 스트림, 값의 흐름과 같이 시간에 따라 연속적으로 발생하는 데이터의 흐름.

옵저버 패턴

  • 데이터의 변화를 감시, 변화에 따라 특정 작업을 수행하는 디자인 패턴.
  • 변화가 일어나면 옵저버(구독자)는 해당 변화에 반응해 알림 받고, 필요한 작업 수행

스트림의 변환과 조작

  • 데이터 스트림을 조작하여 필터링, 매핑, 결합, 변환 등을 수행 -> 새로운 스트림을 생성하는 작업
  • 효과적인 데이터 스트림 처리 및 필요한 형태로의 가공이 가능

바인딩

  • 데이터의 변화와 이에 다른 작업의 연결, 설정 후에는 데이터 변경에 맞춰 UI나 다른 작업을 자동으로 업데이트함

Combine

  • Apple Swift 프로그래밍 언어에 포함된 프레임워크, 비동기적 이벤트 스트림 처리 및 조작에 사용됨.
  • 함수형 프로그래밍과 반응형 프로그래밍 개념을 기반으로, 데이터 스트림을 간단하게 조작하고 조합할 수 있는 도구 제공.
  • 발행자 Publisher : 데이터 스트림 생성, 이벤트를 방출하는 타입. 값 방출이 가능(오류 방출 혹은 작업 완료를 알릴 수 있음)
  • 구독자 Subscriber : 발행자에서 방출되는 이벤트를 받아 처리하는 타입, 값을 받아서 처리, 오류나 작업 완료 이벤트를 처리함
  • 연산자 Operator : 데이터 스트림을 조작하고 변환하기 위한 다양한 연산자(map, filter, flatMap, etc.)가 제공됨.
  • Cancellable : 구독을 취소할 수 있는 타입, 더 이상 이벤트를 받지 않도록 설정할 수 있음
// combine 예시
import Foundation
import Combine

class DataModel {
    @Published var textValue: String = "" 
} // @publsher 프로퍼티 래퍼는 값의 변경이 있을 때마다 해당 값의 변경 사항을 게시함
  // 값이 변경된 것을 newValue 라는 이름의 변수로 게시함
  // 변경되기 전의 값을 oldValue 라는 이름의 변수로 게시

let dataModel = DataModel()

let cancellable = dataModel.$textValue.sink { newValue in
    print("Value changed to: \(newValue)")
} // $textValue는 textValue의 publisher를 의미
  // sink 연산자는 해당 publisher를 구독하고, 값이 변경될 때마다 클로저 내의 코드를 실행하는 연산자

dataModel.textValue = "Hello, Combine!"
dataModel.textValue = "Another value"

/*
출력
Value changed to: 
Value changed to: Hello, Combine!
Value changed to: Another value
*/

RxSwift

Swift 언어로 작성된 함수형 반응형 프로그래밍을 위한 "라이브러리", 옵저버블 시퀀스와 이벤트 기반 프로그래밍을 간편하게 처리할 수 있도록 지원

라이브러리 library

  • 일반적으로 특정 기능을 수행하는, 프로그램에서 재사용 가능한 코드의 모음

  • 개발시간 단축, 효율적인 개발에 도움이 됨

  • 다양한 형태와 목적으로 제공됨

    • 표준 라이브러리 Standard Library : 언어 자체에서 제공하는 기본 함수, 자료구조, 파일 입출력, 문자열 처리 등의 기능을 포함
    • 서드파티 라이브러리 Third-party Library : 개인이나 조직이 개발한 공개 외부 라이브러리
      • 네트워킹, 데이터베이스 연결, UI 구성요소, 이미지 처리, 암호화, 데이터 분석 등 기능이 매우 다양함
  • 보통 함수, 클래스, 모듈, 프레임워크 형태로 제공됨.

    Dependency Manager 의존성 관리 도구

  • 프로젝트에서 사용하는 다양한 라이브러리 및 외부 의존성들을 관리하고, 프로젝트에 효과적으로 통합하기 위한 도구

  • 대표적 Dependency Manager

    • Cocoapods : iOS 및 macOS 애플리 케이션을 위한 의존성 관리 도구.
    • Carthage : Cocoapods과 유사, 프레임워크의 빌드를 사용하여 의존성을 관리. 빌드된 프레임워크를 프로젝트에 수동으로 추가해야 함
    • Swift Package Manager (SPM) : 공식적인 Swift 의존성 관리도구. Swift-only 프로젝트에서 사용할 수 있음.

RxSwift

  • ReactiveX 패턴의 Swift 버전. Swift 로 작성된 반응형 프로그래밍을 위한 라이브러리.
  • 데이터 스트림과 이를 다루는 연산자들을 통해 비동기 및 이벤트 기반 프로그래밍 지원
  • Observable : 데이터 스트림을 나타내는 타입, 데이터의 변화 혹은 이벤트를 방출(emit)
    • Observablenext, error, completed 등의 이벤트를 발생시키는데에 사용됨
  • Observer : Observable에서 방출된 이벤트나 데이터 변화에 반응하는 객체(Subscriber와 비슷한 역할).
    • 데이터의 변화를 감시하고 처리함
  • Operator : Observable을 변형하거나 조작하는 함수, 데이터 스트림 조작에 사용
    • 데이터를 필터링, 변환, 결합, 조작하는 등의 작업을 수행할 수 있음.
  • Scheduler : 비동기 코드의 실행을 관리하는데 사용됨.
    • 메인 스레드에서 UI 업데이트를 수행하거나 백그라운드 스레드에서 작업을 수행하는 등의 일을 스케줄링할 때 사용
  // RxSwift의 예시
import Foundation
import RxSwift
import RxCocoa

class DataModel {
    let textValueSubject = BehaviorSubject<String>(value: "")
    // BehaviorSubject를 사용해 textValueSubject를 초기화
    
    var textValue: Observable<String> {
        return textValueSubject.asObservable()
    } // textValueSubject를 textValue의 Observable로 반환
}

let dataModel = DataModel()

let disposable = dataModel.textValue.subscribe(onNext: { newValue in
    print("Value changed to: \(newValue)")
}) // Subscribe 메소드가 textValue를 구독하고, 값의 변경이 있을 때마다 onNext 클로저 내부 함수를 실행

dataModel.textValueSubject.onNext("Hello, RxSwift!")
dataModel.textValueSubject.onNext("Another value")

/*
출력
Value changed to: 
Value changed to: Hello, RxSwift!
Value changed to: Another value
*/
profile
iOS 개발자가 되고 싶어요

0개의 댓글

Powered by GraphCDN, the GraphQL CDN