|
| 1 | +use core::ptr; |
| 2 | + |
| 3 | +use super::Object; |
| 4 | +use crate::rc::{Id, Shared}; |
| 5 | +use crate::ffi; |
| 6 | + |
| 7 | +/// Associated object support. |
| 8 | +/// |
| 9 | +/// These are associated functions, since they are very rarely used, and will |
| 10 | +/// mostly just clutter up the `Deref` chain and documentation of all other |
| 11 | +/// classes in `icrate`. |
| 12 | +/// |
| 13 | +/// See [Apple's documentation](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html). |
| 14 | +impl Object { |
| 15 | + /// |
| 16 | + /// |
| 17 | + /// # Panics |
| 18 | + /// |
| 19 | + /// This may panic or abort the process if the specified object does not |
| 20 | + /// support associated objects. |
| 21 | + unsafe fn set_associated_ptr<K>(this: &Self, key: &K, value: *const ()) { |
| 22 | + let key: *const K = key; |
| 23 | + // SAFETY: The object and key is non-null |
| 24 | + // |
| 25 | + // Caller ensures that the key is uniquely used for the expected |
| 26 | + // operation. |
| 27 | + unsafe { |
| 28 | + ffi::objc_setAssociatedObject(this.as_ptr(), key.cast(), value as *mut _, ffi::OBJC_ASSOCIATION_ASSIGN) |
| 29 | + } |
| 30 | + } |
| 31 | + |
| 32 | + unsafe fn set_associated_id<K, T>(this: &Self, key: &K, value: Option<&Id<T, Shared>>) { |
| 33 | + let key: *const K = key; |
| 34 | + let ptr: *const T = value.map(|value| Id::as_ptr(value)).unwrap_or(ptr::null()); |
| 35 | + // SAFETY: The object and key is non-null, and the value came from |
| 36 | + // a shared `Id`, so it is safe to retain. |
| 37 | + // |
| 38 | + // Caller ensures that the key is uniquely used for the expected |
| 39 | + // operation. |
| 40 | + unsafe { |
| 41 | + ffi::objc_setAssociatedObject( |
| 42 | + this.as_ptr(), |
| 43 | + key.cast(), |
| 44 | + ptr.cast(), |
| 45 | + ffi::OBJC_ASSOCIATION_RETAIN, |
| 46 | + ) |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + unsafe fn get_associated_ptr<K>(this: &Self, key: &K) -> *const () { |
| 51 | + let key: *const K = key; |
| 52 | + // SAFETY: |
| 53 | + unsafe { ffi::objc_getAssociatedObject(this.as_ptr(), key.cast()).cast() } |
| 54 | + } |
| 55 | + |
| 56 | + unsafe fn get_associated_id<K, T>(this: &Self, key: &K) -> Id<T, Shared> { |
| 57 | + let ptr = this.get_associated_ptr(key) as *mut T; |
| 58 | + // SAFETY: Caller upholds that the associated object stores an `Id`, |
| 59 | + // and that the `Id` was originally `Shared`. |
| 60 | + unsafe { Id::retain_autoreleased(ptr) } |
| 61 | + } |
| 62 | + |
| 63 | + unsafe fn remove_associated<K, T>(this: &Self, key: &K) { |
| 64 | + let key: *const K = key; |
| 65 | + // SAFETY: The object and key is non-null, and the associated is being |
| 66 | + // broken, so the policy doesn't matter. |
| 67 | + // |
| 68 | + // Caller ensures that the key is uniquely used for the expected |
| 69 | + // operation. |
| 70 | + unsafe { |
| 71 | + ffi::objc_setAssociatedObject( |
| 72 | + this.as_ptr(), |
| 73 | + key.cast(), |
| 74 | + ptr::null(), |
| 75 | + ffi::OBJC_ASSOCIATION_ASSIGN, |
| 76 | + ) |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + fn remove_all_associated(this: &Self) { |
| 81 | + // SAFETY: |
| 82 | + unsafe { ffi::objc_removeAssociatedObjects(this.as_ptr()) } |
| 83 | + } |
| 84 | + |
| 85 | + // objc_setAssociatedObject |
| 86 | + // objc_getAssociatedObject |
| 87 | + // objc_removeAssociatedObjects |
| 88 | + |
| 89 | + // https://nshipster.com/associated-objects/ |
| 90 | +} |
0 commit comments