Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

sanichdaniel의 iOS 개발 블로그

일급 시민 함수 - swift 본문

swift

일급 시민 함수 - swift

sanich8355 2020. 10. 14. 19:17
swift에서 함수는 일급 시민이다라는 말은 여러번 들어보았지만, 정작 일급 시민 개념에 대해 완전히 모르는것 같아 공부해보았다.

일급 시민 (First-class Citizen)

다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다.

1. 함수의 인자로 전달

2. 함수에 의해 리턴

3. 변수에 할당 가능

4. 비교 연산이 가능

 

일급 함수 (First-class Function)

함수를 일급 시민으로 취급하는것

 

High Order Function

함수를 인자로 받거나, 함수를 리턴하는 함수

함수가 일급 시민인 언어에서, high order function이 가능하다는것이 보장된다.

-> 함수를 인자로 받거나 리턴할수 있기에

high order function이 가능하다고 함수가 일급시민인것은 아니다

-> 함수의 인자로 함수가 오는것은 특이케이스로 취급된다

 

아래 예시들에서 어떻게 일급 함수를 사용할지 알아보자

1. 함수의 인자로 전달

let subviews = [button, label, imageView]

subviews.forEach { subview in
    view.addSubview(subview)
}

addSubview 함수를 (UIView) -> Void 타입의 클로져로 생각해보자. 

forEach 함수는 (Element) -> Void 타입의 클로져를 인자로 받는다.

따라서 아래처럼 바로 view.addSubview 를 인자로 넘길수 있다.

subviews.forEach(view.addSubview)

조심해야할점은 위 예시처럼 인스턴스 메소드를 클로져처럼 사용하면 인스턴스가 클로져가 살아있는한 메모리상에 같이 남아있게 된다.

위에선 non-escaping 클로져여서 문제가 안되었지만 escaping 클로져에서는 주의

2. 생성자를 인자로 전달

생성자도 일급 시민 함수로 취급할 수 있다

아래 예시에서 생성자를 인자로 전달해보았다

images.map(UIImageView.init)
      .forEach(stackView.addArrangedSubview)

위와 같이 일급 시민 함수를 잘 사용하면 선언적인 코드르 얻을 수 있다.

 

3. 인스턴스 메서드 참조하기

한 클래스의 static 메서드를 호출하려고 클래스 타입까지만 적었는데도, 인스턴스 메서들이 자동완성으로 뜬다

이것은 버그가 아니라, 인스턴스 메서드 하나마다 대응되는 static 메서드가 생기기 때문이다.

(사실 인스턴스 메서드란, 타입 메서드중 인스턴스를 인자로 받고, 인스턴스에 적용될 함수를 리턴하는 Currying 함수다)

출처: oleb.net/blog/2014/07/swift-instance-methods-curried-functions/

이 static 메서드는 인자로 인스턴스를 받는다. 

let closure = UIView.removeFromSuperview(view)

이 기능은 objective-c를 사용하는 target - action을 대체하는데 사용될수있다.

 

해당 타입의 인스턴스 메서드를 리턴하는 typealias 선언.

typealias Action<Type, Input> = (Type) -> (Input) -> Void

ColorPicker는 target action을 추가할수 있는 함수가 있다. observations 변수에서 액션들을 클로져로 들고 있다.

유저가 색깔을 선택하면 pickedColor 함수가 호출되고, 이 함수 내부에서 observations 어레이들을 실행시킨다 

class ColorPicker: UIView {
    private(set) var selectedColor = UIColor.black
    private var observations = [(ColorPicker) -> Void]()

    func addTarget<T: AnyObject>(_ target: T,
                                 action: @escaping Action<T, ColorPicker>) {
        observations.append { [weak target] view in
            guard let target = target else {
                return
            }

            action(target)(view)
        }
    }
    
    func userPickedColor(color: UIColor) {
    	 selectedColor = color
         observations.forEach { $0(self) }
    }
}

* 위 코드는 아래와 같이 리팩토링 가능

observations.append { [weak target] view in
    target.map(action)?(view)
}

이제 뷰컨에서 ColorPicker에 대한 addTarget을 달아주자

class CanvasViewController: UIViewController {
    private var drawingColor = UIColor.black

    func presentColorPicker() {
        let picker = ColorPicker()
        picker.addTarget(self, action: CanvasViewController.colorPicked)
        view.addSubview(picker)
    }

    private func colorPicked(using picker: ColorPicker) {
        drawingColor = picker.selectedColor
    }
}

이제 유저가 피커에서 색깔을 선택할때마다, 뷰컨의 인스턴스 함수 colorPicked 함수가 호출이된다!

 

 

출처

www.swiftbysundell.com/articles/first-class-functions-in-swift/

'swift' 카테고리의 다른 글

빌드 타임 개선  (0) 2020.10.14
Pure functions in swift  (0) 2020.10.14
Identifiable  (0) 2020.10.07
Hashable  (0) 2020.10.07
KeyPath  (0) 2020.10.03