🐰

Objective-C黒魔術を駆使してあるオブジェクトに別のオブジェクトを追加するやつ

2024/01/12に公開

黒魔術とはObjective-C Runtime APIのobjc_setAssociatedObjectobjc_getAssociatedObjectのことですが、久々にSwift(5)から使おうとしたらRawPointer周りの扱いが少し変わっていたので、備忘録として残しておきます。

import Foundation
import ObjectiveC

extension NSString {
	
	private static let _associatedObjectKey = malloc(1)!
	
	var customObject: Date? {
		get {
			value(forKey: Self._associatedObjectKey)
		}
		set {
			setAssign(value: newValue, forKey: Self._associatedObjectKey)
		}
	}
	
}

extension NSObject {
	
	func value<T>(forKey key: UnsafeRawPointer) -> T? {
		objc_getAssociatedObject(self, key) as? T
	}
	
	func setAssign<T>(value: T?, forKey key: UnsafeRawPointer) {
		objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_ASSIGN)
	}
	
	func setRetainNonAtomic<T>(value: T?, forKey key: UnsafeRawPointer) {
		objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
	}
	
	func setCopyNonAtomic<T>(value: T?, forKey key: UnsafeRawPointer) {
		objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_COPY_NONATOMIC)
	}
	
	func setRetain<T>(value: T?, forKey key: UnsafeRawPointer) {
		objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_RETAIN)
	}
	
	func setCopy<T>(value: T?, forKey key: UnsafeRawPointer) {
		objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_COPY)
	}
	
}

NSObjectの拡張として実装をまとめたのでNSObject継承ツリー配下のクラスにしか使えませんが、この辺りは適宜実装方法を変えれば良いと思います。

キーの定義方法はこのフォーラムの投稿を参考に、malloc(1)で生成する方法にしました。
https://forums.swift.org/t/handling-the-new-forming-unsaferawpointer-warning/65523/7

private static let _associatedObjectKey = malloc(1)!

公式資料:

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

Discussion