Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Extension에 Stored Property 추가 방법 (objc_getAssociatedObject, objc_setAssociatedObject) 본문

iOS 응용 (swift)

[iOS - swift] Extension에 Stored Property 추가 방법 (objc_getAssociatedObject, objc_setAssociatedObject)

jake-kim 2023. 1. 20. 21:07

extension에서의 stored property

  • extension에서는 stored property 선언이 불가능
    • 불가능한 이유? 만약 가능하다면 Int와 같은곳에도 String같은 값을 stored property로 추가할수 있게되어, Int의 본질적인 상태(메모리 크기, 역할)을 extension으로 어디에서든지 변경하여 예측불가능한 코드가 될 수 있으므로 애초에 extension에서 stored property선언을 막은 것

AssociatedObject를 사용한 프로퍼티 추가

  • objective c에서 제공했던 setter, getter를 사용하여 swift의 extension에도 stored property 추가가 가능
  • 정확히는 computed proprety를 만들지만, get, set할 때 내부적으로 저장하고 있는 key에 관한 값을 retain하고 있다가 반환하는 것

AssociatedObject 주요 메소드

  • 객체를 저장하는 메소드
// https://developer.apple.com/documentation/objectivec/1418509-objc_setassociatedobject

func objc_setAssociatedObject(
    _ object: Any,
    _ key: UnsafeRawPointer,
    _ value: Any?,
    _ policy: objc_AssociationPolicy
)
  • 객체를 가져오는 메소드
// https://developer.apple.com/documentation/objectivec/1418865-objc_getassociatedobject

func objc_getAssociatedObject(
    _ object: Any,
    _ key: UnsafeRawPointer
) -> Any?
  • 해당 값을 지우는 방법은 nil을 할당하여 삭제
    • removeAssociatedObjects라는 것을 사용하면 의도하지 않은 다른 값도 지워질 수 있으므로 nil을 할당하는 것이 안전

https://developer.apple.com/documentation/objectivec/1418683-objc_removeassociatedobjects

extension에 stored property 추가 방법

  • UIScrollView를 사용할 때, 이전의 offset을 따로 관리하고 싶은 경우, extension으로 lastOffsetY라는 것을 정의
  • AssociatedKeys 구조체를 사용하고, 내부에는 static var인 키 값을 입력
private struct AssociatedKeys {
    static var lastOffsetY = "lastOffsetY"
}

 

  • 위에서 선언한 key값을 가지고 getter, setter를 정의
extension UIScrollView {
    var lastOffsetY: CGFloat {
        get {
            (objc_getAssociatedObject(self, &AssociatedKeys.lastOffsetY) as? CGFloat) ?? 0.0
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.lastOffsetY, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

위에서 추가한 property 사용하기

ex) 스크롤뷰에서 lastOffsetY를 사용하여 more scroll(데이터 조회하기 위해 손가락을 아래에서 위로 스와이프)인지 less score인지 확인

  • 뷰 선언
import UIKit

class ViewController: UIViewController {
    private let scrollView: UIScrollView = {
        let view = UIScrollView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private let label: UILabel = {
        let label = UILabel()
        label.text = "jake iOS app 알아가기 \n\n example text \n\n\n 123 \n\n\n example1 jake iOS app 알아가기 \n\n example text \n\n\n 123 \n\n\n example1 jake iOS app 알아가기 \n\n example text \n\n\n 123 \n\n\n example1 jake iOS app 알아가기 \n\n example text \n\n\n 123 \n\n\n example1 jake iOS app 알아가기 \n\n example text \n\n\n 123 \n\n\n example1 \n\n example text \n\n\n 123 \n\n\n example1 jake iOS app 알아가기 \n\n example text \n\n\n 123 \n\n\n example1"
        label.textColor = .black
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(scrollView)
        scrollView.addSubview(label)
        
        NSLayoutConstraint.activate([
            scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
            scrollView.rightAnchor.constraint(equalTo: view.rightAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
        ])
        NSLayoutConstraint.activate([
            label.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
            label.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
            label.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            label.topAnchor.constraint(equalTo: scrollView.topAnchor),
            label.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])
        
        scrollView.delegate = self
    }
}
  • 스크롤뷰 델리게이트에서 lastOffsetY 값 저장 및 사용
extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        defer { scrollView.lastOffsetY = scrollView.contentOffset.y }
        
        guard scrollView.lastOffsetY != 0 else {
            print("more scroll")
            return
        }
        
        if scrollView.lastOffsetY < scrollView.contentOffset.y {
            print("more scroll")
        } else {
            print("less scroll")
        }
        
    }
}

(완성)

 

* 전체 코드: https://github.com/JK0369/ExExtensionStoredProperty

* 참고

https://developer.apple.com/documentation/objectivec/1418683-objc_removeassociatedobjects

https://developer.apple.com/documentation/objectivec/1418865-objc_getassociatedobject

https://developer.apple.com/documentation/objectivec/1418509-objc_setassociatedobject

Comments