use std::cell::UnsafeCell;
use std::ptr;
use {Id, ShareId, Message, ToMessage};
use runtime::Object;
#[link(name = "objc", kind = "dylib")]
extern {
fn objc_storeWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object;
fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object;
fn objc_destroyWeak(addr: *mut *mut Object);
}
pub struct WeakId<T> {
ptr: Box<UnsafeCell<*const T>>,
}
impl<T> WeakId<T> where T: Message {
pub fn new(obj: &ShareId<T>) -> WeakId<T> {
let ptr = Box::new(UnsafeCell::new(ptr::null()));
unsafe {
let loc = ptr.get() as *mut *mut Object;
objc_storeWeak(loc, obj.as_id_ptr());
}
WeakId { ptr: ptr }
}
pub fn load(&self) -> Option<ShareId<T>> {
unsafe {
let loc = self.ptr.get() as *mut *mut Object;
let obj = objc_loadWeakRetained(loc) as *mut T;
if obj.is_null() {
None
} else {
Some(Id::from_retained_ptr(obj))
}
}
}
}
#[unsafe_destructor]
impl<T> Drop for WeakId<T> {
fn drop(&mut self) {
unsafe {
let loc = self.ptr.get() as *mut *mut Object;
objc_destroyWeak(loc);
}
}
}
unsafe impl<T> Sync for WeakId<T> where T: Sync { }
unsafe impl<T> Send for WeakId<T> where T: Sync { }
#[cfg(test)]
mod tests {
use runtime::Object;
use test_utils;
use super::WeakId;
#[test]
fn test_weak() {
let obj = test_utils::sample_object().share();
let weak = WeakId::new(&obj);
let strong = weak.load().unwrap();
assert!(&*strong as *const Object == &*obj as *const Object);
drop(strong);
drop(obj);
assert!(weak.load().is_none());
}
}