use core::fmt;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use std::panic::{RefUnwindSafe, UnwindSafe};
use super::AutoreleasePool;
use super::{Owned, Ownership, Shared};
use crate::ffi;
use crate::Message;
#[repr(transparent)]
pub struct Id<T: ?Sized, O: Ownership> {
ptr: NonNull<T>,
item: PhantomData<T>,
own: PhantomData<O>,
}
impl<T: Message + ?Sized, O: Ownership> Id<T, O> {
#[inline]
pub unsafe fn new(ptr: NonNull<T>) -> Id<T, O> {
Self {
ptr,
item: PhantomData,
own: PhantomData,
}
}
}
impl<T: Message, O: Ownership> Id<T, O> {
#[doc(alias = "objc_retain")]
#[cfg_attr(not(debug_assertions), inline)]
pub unsafe fn retain(ptr: NonNull<T>) -> Id<T, O> {
let ptr = ptr.as_ptr() as *mut ffi::objc_object;
let res = unsafe { ffi::objc_retain(ptr) };
debug_assert_eq!(res, ptr, "objc_retain did not return the same pointer");
let res = unsafe { NonNull::new_unchecked(res as *mut T) };
unsafe { Self::new(res) }
}
#[cfg_attr(not(debug_assertions), inline)]
fn autorelease_inner(self) -> *mut T {
let ptr = ManuallyDrop::new(self).ptr.as_ptr() as *mut ffi::objc_object;
let res = unsafe { ffi::objc_autorelease(ptr) };
debug_assert_eq!(res, ptr, "objc_autorelease did not return the same pointer");
res as *mut T
}
}
impl<T: Message> Id<T, Owned> {
#[doc(alias = "objc_autorelease")]
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
#[inline]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::mut_from_ref)]
pub fn autorelease<'p>(self, pool: &'p AutoreleasePool) -> &'p mut T {
let ptr = self.autorelease_inner();
unsafe { pool.ptr_as_mut(ptr) }
}
#[inline]
pub unsafe fn from_shared(obj: Id<T, Shared>) -> Self {
let ptr = ManuallyDrop::new(obj).ptr;
unsafe { <Id<T, Owned>>::new(ptr) }
}
}
impl<T: Message> Id<T, Shared> {
#[doc(alias = "objc_autorelease")]
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
#[inline]
#[allow(clippy::needless_lifetimes)]
pub fn autorelease<'p>(self, pool: &'p AutoreleasePool) -> &'p T {
let ptr = self.autorelease_inner();
unsafe { pool.ptr_as_ref(ptr) }
}
}
impl<T: Message + ?Sized> From<Id<T, Owned>> for Id<T, Shared> {
#[inline]
fn from(obj: Id<T, Owned>) -> Self {
let ptr = ManuallyDrop::new(obj).ptr;
unsafe { <Id<T, Shared>>::new(ptr) }
}
}
impl<T: Message> Clone for Id<T, Shared> {
#[doc(alias = "objc_retain")]
#[doc(alias = "retain")]
#[inline]
fn clone(&self) -> Self {
unsafe { Id::retain(self.ptr) }
}
}
impl<T: ?Sized, O: Ownership> Drop for Id<T, O> {
#[doc(alias = "objc_release")]
#[doc(alias = "release")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_release(self.ptr.as_ptr() as *mut _) };
}
}
unsafe impl<T: Sync + Send + ?Sized> Send for Id<T, Shared> {}
unsafe impl<T: Sync + Send + ?Sized> Sync for Id<T, Shared> {}
unsafe impl<T: Send + ?Sized> Send for Id<T, Owned> {}
unsafe impl<T: Sync + ?Sized> Sync for Id<T, Owned> {}
impl<T: ?Sized, O: Ownership> Deref for Id<T, O> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized> DerefMut for Id<T, Owned> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { self.ptr.as_mut() }
}
}
impl<T: ?Sized, O: Ownership> fmt::Pointer for Id<T, O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr.as_ptr(), f)
}
}
impl<T: ?Sized, O: Ownership> Unpin for Id<T, O> {}
impl<T: RefUnwindSafe + ?Sized, O: Ownership> RefUnwindSafe for Id<T, O> {}
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Id<T, Shared> {}
impl<T: UnwindSafe + ?Sized> UnwindSafe for Id<T, Owned> {}
#[cfg(test)]
mod tests {
use core::ptr::NonNull;
use super::{Id, Owned, Shared};
use crate::rc::autoreleasepool;
use crate::runtime::Object;
use crate::{class, msg_send};
fn retain_count(obj: &Object) -> usize {
unsafe { msg_send![obj, retainCount] }
}
#[test]
fn test_autorelease() {
let obj: Id<Object, Shared> = unsafe { Id::new(msg_send![class!(NSObject), new]) };
let cloned = obj.clone();
autoreleasepool(|pool| {
let _ref = obj.autorelease(pool);
assert_eq!(retain_count(&*cloned), 2);
});
assert_eq!(retain_count(&*cloned), 1);
}
#[test]
fn test_clone() {
let cls = class!(NSObject);
let obj: Id<Object, Owned> = unsafe {
let obj: *mut Object = msg_send![cls, alloc];
let obj: *mut Object = msg_send![obj, init];
Id::new(NonNull::new_unchecked(obj))
};
assert_eq!(retain_count(&obj), 1);
let obj: Id<_, Shared> = obj.into();
assert_eq!(retain_count(&obj), 1);
let cloned = obj.clone();
assert_eq!(retain_count(&cloned), 2);
assert_eq!(retain_count(&obj), 2);
drop(obj);
assert_eq!(retain_count(&cloned), 1);
}
}