-
(Swift) closure @escapingiOS💖 2024. 10. 16. 17:43
비동기 HTTP Request CompletionHandler 에서 보았던 @escaping
클로저 ?
- 이름이 있는 함수를 만들지 않고, 실행되는 그룹 코드
- 전달 될 수 도, 코드 안에서 사용 될 수 도 있는 코드
클로저 표현식
// 1 { (매개변수) -> Void in code } // 2 { () -> () in code }클로저 @escaping
- 클로저가 함수의 인수로 전달되지만, 함수가 return된 후에 호출되는 경우
- 이 경우, 클로저는 함수를 escaping 한다고 한다.
- 클로저를 매개변수 중 하나로 취하는 함수를 선언할 때, 매개변수 유형 앞에 @escaping 작성
- 클로저 @escaping 될 수 있는 한 가지 방법
- 함수 외부에서 정의된 변수에 저장 되는 것
- 예를 들어, 비동기 작업을 시작하는 함수들은 대부분 handler 로 클로저 인수를 사용함
- ➡️ 함수는 작업 완료 후 return 하지만, 클로저는 함수가 return 될 때 까지 호출 되지 X
- 함수 외부에서 정의된 변수에 저장 되는 것
- escaping closure에서, self 변수를 capture 하려면, 명시적으로 self 붙여야함!!
- ➡️ 실수로 strong 참조 cycle을 생성하여, 메모리 누수 될 수 있으니!
- 일반 closure 에서는, 해당 closure 본문에서 변수를 사용해, 암묵적으로 해당 변수를 캡쳐하기에 self 생략 가능
공식 문서 예제
var completionHandlers: [() -> Void] = [] func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) } // someFunctionWithEscapingClosure 함수는 클로저를 인수로 받아서 함수 외부에서 선언된 배열에 추가 // 이 함수의 매개변수를 @escaping으로 표시하지 않으면 컴파일 타임 오류가 발생 func someFunctionWithNonescapingClosure(closure: () -> Void) { closure() } class SomeClass { var x = 10 func doSomething() { someFunctionWithEscapingClosure { self.x = 100 } // 실행 순서 // 1. someFunctionWithEscapingClosure() 함수 호출됨 & 인수로 escaping 클로저 {self.x = 100} 를 받음 // 2. someFunctionWithEscapingClosure() 함수 코드 실행 (completionHandlers에 {self.x = 100} 가 append 됨 // 3. someFunctionWithEscapingClosure() 함수 코드 끝났으니 return // 4. 이제 escsping closure {self.x = 100} 실행 // 5. x == 100 이 됨 // 🔺 escaping closure에서, self 변수를 capture 하려면, 명시적으로 self 붙여야함!! someFunctionWithNonescapingClosure { x = 200 } // x == 200 } } let instance = SomeClass() instance.doSomething() // completionHandlers는 현재 [{self.x = 100}] print(instance.x) // Prints "200" completionHandlers.first?() // {self.x = 100} 실행해라 print(instance.x) // Prints "100"개인 예제
struct UserProfile: Codable { let loginId: String let avatarUrl: String let followers: Int let following: Int enum codingKeys: String, CodingKey { case loginId = "login" case avatarUrl = "avatar_url" case followers case following } } final class NetworkService { let session: URLSession init(configuration: URLSessionConfiguration) { session = URLSession(configuration: configuration) } ⭐️ func fetchProfile(userName: String, completion: @escaping (Result<UserProfile, Error>) -> Void { let urlString = "https://xxxx.xxxxx.com/users/\(userName)" let url = URL(string: urlString)! let task = session.dataTask(with: url) { data, response, error in if let error = error { // nil이 아닌경우, 즉 error에 값이 있는 경우 completion(.failure(error)) return } if let response = response as? HTTPURLResponse, !(200..<300).contains(response.statusCode) { completion(.failure(error)) return } guard let data = data else { // data is optional completion(.failure(error)) return } do { let decoder = JSONDecoder() let profile = try decoder.decoder(UserProfile.self, from: data) completion(.success(profile)) } catch let error { completion(.failure(error)) } } task.resume() } } let networkService = NetworkService(configuration: .default) // NetworkService 객체 사용 기본형 // networkService.fetchProfile(userName: "aaa", completion: (Result) -> Void) networkService.fetchProfile(userName: "hortenssiaa") { result in // result 는 Result 타입이므로, success / failure 두가지 case로 나뉜다 switch result { case .success(let profile): print("[Profile]: \(profile)") case .failure(let error): print("[Error]: \(error)") } }Documentation
docs.swift.org
'iOS💖' 카테고리의 다른 글
[사전캠프 TIL 1일차] Struct / Class / Protocol (0) 2025.02.03 [Swift] Network connect 두 가지 방법 (0) 2024.10.22 (Swift) the concept of capturing (capturing 개념) (2) 2024.10.16 (Swift) 메모리 참조 속성 (strong, week, unowned, optional unowned) (3) 2024.10.16 (Swift) ARC (Automatic Reference Counting, 자동 참조 계산) (1) 2024.10.15