목적 C에서 오는 objc_setAssociatedObject
두 객체 사이의 함수 를 호출 하여 참조를 유지하도록 할 수 있습니다. 런타임시 참조가 제거 될 때까지 객체가 파괴되는 것을 원하지 않는 경우 유용 할 수 있습니다. 스위프트가 이와 비슷한 것이 있습니까?
답변
다음은 jckarter의 답변 에서 파생 된 간단하지만 완전한 예제 입니다.
기존 클래스에 새 속성을 추가하는 방법을 보여줍니다. 확장 블록에서 계산 된 속성을 정의하여 수행합니다. 계산 된 속성은 관련 객체로 저장됩니다.
import ObjectiveC
// Declare a global var to produce a unique address as the assoc object handle
private var AssociatedObjectHandle: UInt8 = 0
extension MyClass {
var stringProperty:String {
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! String
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
편집하다:
초기화되지 않은 속성의 값을 가져오고 오류가 발생하지 않도록 지원해야하는 경우 다음 unexpectedly found nil while unwrapping an Optional value
과 같이 getter를 수정할 수 있습니다.
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""
}
답변
이 솔루션은 String, Int, Double 등과 같이 자동으로 브리지 되는 값 유형 뿐만 아니라 모든 값 유형 도 지원합니다 .
포장기
import ObjectiveC
final class Lifted<T> {
let value: T
init(_ x: T) {
value = x
}
}
private func lift<T>(x: T) -> Lifted<T> {
return Lifted(x)
}
func setAssociatedObject<T>(object: AnyObject, value: T, associativeKey: UnsafePointer<Void>, policy: objc_AssociationPolicy) {
if let v: AnyObject = value as? AnyObject {
objc_setAssociatedObject(object, associativeKey, v, policy)
}
else {
objc_setAssociatedObject(object, associativeKey, lift(value), policy)
}
}
func getAssociatedObject<T>(object: AnyObject, associativeKey: UnsafePointer<Void>) -> T? {
if let v = objc_getAssociatedObject(object, associativeKey) as? T {
return v
}
else if let v = objc_getAssociatedObject(object, associativeKey) as? Lifted<T> {
return v.value
}
else {
return nil
}
}
가능한
클래스 확장 (사용 예)
extension UIView {
private struct AssociatedKey {
static var viewExtension = "viewExtension"
}
var referenceTransform: CGAffineTransform? {
get {
return getAssociatedObject(self, associativeKey: &AssociatedKey.viewExtension)
}
set {
if let value = newValue {
setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.viewExtension, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
답변
분명히 이것은 Objective-C 객체에서만 작동합니다. 이것에 대해 조금 다루고 나면 Swift에서 호출하는 방법은 다음과 같습니다.
import ObjectiveC
// Define a variable whose address we'll use as key.
// "let" doesn't work here.
var kSomeKey = "s"
…
func someFunc() {
objc_setAssociatedObject(target, &kSomeKey, value, UInt(OBJC_ASSOCIATION_RETAIN))
let value : AnyObject! = objc_getAssociatedObject(target, &kSomeKey)
}
답변
Swift 3.0 업데이트 예를 들어 이것은 UITextField입니다.
import Foundation
import UIKit
import ObjectiveC
// Declare a global var to produce a unique address as the assoc object handle
var AssociatedObjectHandle: UInt8 = 0
extension UITextField
{
var nextTextField:UITextField {
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! UITextField
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
답변
Swift 5.1이 필요한 현대적인 도우미를 작성했습니다.
/*
AssociatedObject.swift
Copyright © 2020 RFUI.
https://github.com/BB9z/iOS-Project-Template
The MIT License
https://opensource.org/licenses/MIT
*/
import Foundation
/**
Objective-C associated value wrapper.
Usage
```
private let fooAssociation = AssociatedObject<String>()
extension SomeObject {
var foo: String? {
get { fooAssociation[self] }
set { fooAssociation[self] = newValue }
}
}
```
*/
public final class AssociatedObject<T> {
private let policy: objc_AssociationPolicy
/// Creates an associated value wrapper.
/// - Parameter policy: The policy for the association.
public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
self.policy = policy
}
/// Accesses the associated value.
/// - Parameter index: The source object for the association.
public subscript(index: AnyObject) -> T? {
get { objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as? T }
set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
}
}
Swift 구조를 무료로 지원한다는 사실에 놀랄 수도 있습니다.
답변
Klaas는 Swift 2.1에 대해서만 답변합니다.
import ObjectiveC
let value = NSUUID().UUIDString
var associationKey: UInt8 = 0
objc_setAssociatedObject(parentObject, &associationKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
let fetchedValue = objc_getAssociatedObject(parentObject, &associationKey) as! String
답변
#import <objc/runtime.h>
브리징 헤더 파일을 추가 하여 신속한 코드로 objc_setAssociatedObject에 액세스하십시오.
