pub struct Id<T: ?Sized> { /* private fields */ }
Expand description
A reference counted pointer type for Objective-C objects.
Id
strongly references or “retains” the given object T
, and
decrements the retain count or “releases” it again when dropped, thereby
ensuring it will be deallocated at the right time.
The type T
inside Id<T>
can be anything that implements Message
.
T
’s ClassType
implementation (if any) determines whether it is
mutable, and by extension whether Id<T>
is mutable.
This can usually be gotten from one of the methods in icrate
, but can be
created manually with the msg_send_id!
macro (or even more manually,
with the Id::new
, Id::retain
or Id::retain_autoreleased
methods).
Comparison to std
types
Id<T>
can be thought of as kind of a weird combination of Arc
and
Box
:
If T
implements IsMutable
(like it does on NSMutableString
and
NSMutableArray<_>
), Id<T>
acts like Box<T>
, and allows mutable /
unique access to the type.
Otherwise, which is the most common case, Id<T>
acts like Arc<T>
, and
allows cloning by bumping the reference count.
Forwarding implementations
Since Id<T>
is a smart pointer, it Deref
s to T
, and similarly
implements DerefMut
when mutable.
It also forwards the implementation of a bunch of standard library traits
such as PartialEq
, AsRef
, and so on, so that it becomes possible
to use e.g. Id<NSString>
as if it was NSString
. (Having NSString
directly is not possible since Objective-C objects cannot live on the
stack, but instead must reside on the heap).
Note that because of current limitations in the Rust trait system, some
traits like Default
, IntoIterator
, FromIterator
, From
and
Into
are not directly implementable on NSString
; for that use-case,
we instead provide the DefaultId
, IdIntoIterator
and
IdFromIterator
traits, which make some of the the aforementioned
traits implementable on Id
.
Memory layout
This is guaranteed to have the same size and alignment as a pointer to the
object, *const T
.
Additionally, it participates in the null-pointer optimization, that is,
Option<Id<T>>
is guaranteed to have the same size as Id<T>
.
Example
Various usage of Id
on an immutable object.
use icrate::Foundation::{NSObject, NSString};
use objc2::rc::Id;
use objc2::{ClassType, msg_send_id};
// Use `msg_send_id!` to create an `Id` with correct memory management
//
// SAFETY: The types are correct, and it is safe to call the `new`
// selector on `NSString`.
let string: Id<NSString> = unsafe { msg_send_id![NSString::class(), new] };
// Or:
// let string = NSString::new();
// Methods on `NSString` is usable via. `Deref`
#[cfg(not_available)]
assert_eq!(string.len(), 0);
// Bump the reference count of the object (possible because the object is
// immutable, would not be possible for `NSMutableString`).
let another_ref: Id<NSString> = string.clone();
// Convert one of the references to a reference to `NSObject` instead
let obj: Id<NSObject> = Id::into_super(string);
// And use the `Debug` impl from that
assert_eq!(format!("{obj:?}"), "");
// Finally, the `Id`s go out of scope, the reference counts are decreased,
// and the string will deallocate
Implementations§
source§impl<T: ?Sized + Message> Id<T>
impl<T: ?Sized + Message> Id<T>
sourcepub unsafe fn new(ptr: *mut T) -> Option<Self>
pub unsafe fn new(ptr: *mut T) -> Option<Self>
Construct an Id
from a pointer that already has +1 retain count.
Returns None
if the pointer was NULL.
This is useful when you have a retain count that has been handed off
from somewhere else, usually Objective-C methods like init
, alloc
,
new
, copy
, or methods with the ns_returns_retained
attribute.
Safety
You must uphold the same requirements as described in Id::retain
.
Additionally, you must ensure the given object pointer has +1 retain count.
Example
Comparing different ways of creating a new NSObject
.
use objc2::rc::Id;
use objc2::runtime::NSObject;
use objc2::{msg_send, msg_send_id, ClassType};
// Manually using `msg_send!` and `Id::new`
let obj: *mut NSObject = unsafe { msg_send![NSObject::class(), alloc] };
let obj: *mut NSObject = unsafe { msg_send![obj, init] };
// SAFETY: `-[NSObject init]` returns +1 retain count
let obj: Id<NSObject> = unsafe { Id::new(obj).unwrap() };
// Or with `msg_send_id!`
let obj: Id<NSObject> = unsafe { msg_send_id![NSObject::alloc(), init] };
// Or using the `NSObject::new` method
let obj = NSObject::new();
sourcepub fn as_ptr(this: &Self) -> *const T
pub fn as_ptr(this: &Self) -> *const T
Returns a raw pointer to the object.
The pointer is valid for at least as long as the Id
is held.
See Id::as_mut_ptr
for the mutable equivalent.
This is an associated method, and must be called as Id::as_ptr(obj)
.
sourcepub fn as_mut_ptr(this: &mut Self) -> *mut Twhere
T: IsMutable,
pub fn as_mut_ptr(this: &mut Self) -> *mut Twhere
T: IsMutable,
Returns a raw mutable pointer to the object.
The pointer is valid for at least as long as the Id
is held.
See Id::as_ptr
for the immutable equivalent.
This is an associated method, and must be called as
Id::as_mut_ptr(obj)
.
source§impl<T: Message> Id<T>
impl<T: Message> Id<T>
sourcepub unsafe fn cast<U: Message>(this: Self) -> Id<U> ⓘ
pub unsafe fn cast<U: Message>(this: Self) -> Id<U> ⓘ
Convert the type of the given object to another.
This is equivalent to a cast
between two pointers.
See Id::into_super
and ProtocolObject::from_id
for safe
alternatives.
This is common to do when you know that an object is a subclass of
a specific class (e.g. casting an instance of NSString
to NSObject
is safe because NSString
is a subclass of NSObject
).
All 'static
objects can safely be cast to AnyObject
, since that
assumes no specific class.
Safety
You must ensure that the object can be reinterpreted as the given type.
If T
is not 'static
, you must ensure that U
ensures that the
data contained by T
is kept alive for as long as U
lives.
Additionally, you must ensure that any safety invariants that the new type has are upheld.
Note that it is not in general safe to cast e.g. Id<NSString>
to
Id<NSMutableString>
, even if you’ve checked at runtime that the
object is an instance of NSMutableString
! This is because
Id<NSMutableString>
assumes the string is unique, whereas it may
have been cloned while being an Id<NSString>
.
sourcepub unsafe fn retain(ptr: *mut T) -> Option<Id<T>>
pub unsafe fn retain(ptr: *mut T) -> Option<Id<T>>
Retain the pointer and construct an Id
from it.
Returns None
if the pointer was NULL.
This is useful when you have been given a pointer to an object from some API, and you would like to ensure that the object stays around while you work on it.
For normal Objective-C methods, you may want to use
Id::retain_autoreleased
instead, as that is usually more
performant.
See ClassType::retain
for a safe alternative.
Safety
If the object is mutable, the caller must ensure that there are no
other pointers or references to the object, such that the returned
Id
pointer is unique.
Additionally, the pointer must be valid as a reference (aligned,
dereferencable and initialized, see the std::ptr
module for more
information) or NULL.
Finally, you must ensure that any data that T
may reference lives
for at least as long as T
.
sourcepub unsafe fn retain_autoreleased(ptr: *mut T) -> Option<Id<T>>
pub unsafe fn retain_autoreleased(ptr: *mut T) -> Option<Id<T>>
Retains a previously autoreleased object pointer.
This is useful when calling Objective-C methods that return autoreleased objects, see Cocoa’s Memory Management Policy.
This has exactly the same semantics as Id::retain
, except it can
sometimes avoid putting the object into the autorelease pool, possibly
yielding increased speed and reducing memory pressure.
Note: This relies heavily on being inlined right after msg_send!
,
be careful to not accidentally require instructions between these.
Safety
Same as Id::retain
.
sourcepub fn autorelease<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p T
pub fn autorelease<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p T
Autoreleases the Id
, returning a reference bound to the pool.
The object is not immediately released, but will be when the innermost / current autorelease pool (given as a parameter) is drained.
See Id::autorelease_mut
for the mutable alternative.
This is an associated method, and must be called as
Id::autorelease(obj, pool)
.
sourcepub fn autorelease_mut<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p mut Twhere
T: IsMutable,
pub fn autorelease_mut<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p mut Twhere
T: IsMutable,
Autoreleases the Id
, returning a mutable reference bound to the
pool.
The object is not immediately released, but will be when the innermost / current autorelease pool (given as a parameter) is drained.
See Id::autorelease
for the immutable alternative.
This is an associated method, and must be called as
Id::autorelease_mut(obj, pool)
.
sourcepub fn autorelease_return(this: Self) -> *mut T
pub fn autorelease_return(this: Self) -> *mut T
Autoreleases and prepares the Id
to be returned to Objective-C.
The object is not immediately released, but will be when the innermost autorelease pool is drained.
This is useful when declaring your own methods where you will often find yourself in need of returning autoreleased objects to properly follow Cocoa’s Memory Management Policy.
To that end, you could use Id::autorelease
, but that would require
you to have an AutoreleasePool
object at hand, which you often
won’t have in such cases. This function doesn’t require a pool
object (but as a downside returns a pointer instead of a reference).
This is also more efficient than a normal autorelease
, since it
makes a best effort attempt to hand off ownership of the retain count
to a subsequent call to objc_retainAutoreleasedReturnValue
/
Id::retain_autoreleased
in the enclosing call frame.
This optimization relies heavily on this function being tail called, so make sure you only call this function at the end of your method.
Example
Returning an Id
from a declared method (note: the declare_class!
macro supports doing this for you automatically).
use objc2::{class, msg_send_id, sel};
use objc2::declare::ClassBuilder;
use objc2::rc::Id;
use objc2::runtime::{AnyClass, AnyObject, Sel};
let mut builder = ClassBuilder::new("ExampleObject", class!(NSObject)).unwrap();
extern "C" fn get(cls: &AnyClass, _cmd: Sel) -> *mut AnyObject {
let obj: Id<AnyObject> = unsafe { msg_send_id![cls, new] };
Id::autorelease_return(obj)
}
unsafe {
builder.add_class_method(
sel!(get),
get as extern "C" fn(_, _) -> _,
);
}
let cls = builder.register();
Trait Implementations§
source§impl<T: ?Sized + IsMutable> BorrowMut<T> for Id<T>
impl<T: ?Sized + IsMutable> BorrowMut<T> for Id<T>
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T: BufRead + ?Sized + IsMutable> BufRead for Id<T>
impl<T: BufRead + ?Sized + IsMutable> BufRead for Id<T>
source§fn fill_buf(&mut self) -> Result<&[u8]>
fn fill_buf(&mut self) -> Result<&[u8]>
source§fn consume(&mut self, amt: usize)
fn consume(&mut self, amt: usize)
amt
bytes have been consumed from the buffer,
so they should no longer be returned in calls to read
. Read moresource§fn read_line(&mut self, buf: &mut String) -> Result<usize>
fn read_line(&mut self, buf: &mut String) -> Result<usize>
0xA
byte) is reached, and append
them to the provided String
buffer. Read moresource§fn has_data_left(&mut self) -> Result<bool, Error>
fn has_data_left(&mut self) -> Result<bool, Error>
buf_read_has_data_left
)Read
has any data left to be read. Read moresource§fn skip_until(&mut self, byte: u8) -> Result<usize, Error>
fn skip_until(&mut self, byte: u8) -> Result<usize, Error>
bufread_skip_until
)byte
or EOF is reached. Read moresource§impl<T: ?Sized> Drop for Id<T>
impl<T: ?Sized> Drop for Id<T>
#[may_dangle]
(see this) doesn’t apply here since we
don’t run T
’s destructor (rather, we want to discourage having T
s with
a destructor); and even if we did run the destructor, it would not be safe
to add since we cannot verify that a dealloc
method doesn’t access
borrowed data.
source§impl<T: Error + ?Sized> Error for Id<T>
impl<T: Error + ?Sized> Error for Id<T>
source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · source§fn description(&self) -> &str
fn description(&self) -> &str
source§impl<T, U: IdFromIterator<T>> FromIterator<T> for Id<U>
impl<T, U: IdFromIterator<T>> FromIterator<T> for Id<U>
source§fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self
source§impl<T: Hasher + ?Sized + IsMutable> Hasher for Id<T>
impl<T: Hasher + ?Sized + IsMutable> Hasher for Id<T>
source§fn write_u128(&mut self, i: u128)
fn write_u128(&mut self, i: u128)
u128
into this hasher.source§fn write_usize(&mut self, i: usize)
fn write_usize(&mut self, i: usize)
usize
into this hasher.source§fn write_i128(&mut self, i: i128)
fn write_i128(&mut self, i: i128)
i128
into this hasher.source§fn write_isize(&mut self, i: isize)
fn write_isize(&mut self, i: isize)
isize
into this hasher.source§fn write_length_prefix(&mut self, len: usize)
fn write_length_prefix(&mut self, len: usize)
hasher_prefixfree_extras
)source§impl<'a, T: ?Sized> IntoIterator for &'a Id<T>where
&'a T: IntoIterator,
impl<'a, T: ?Sized> IntoIterator for &'a Id<T>where
&'a T: IntoIterator,
source§impl<'a, T: ?Sized + IsMutable> IntoIterator for &'a mut Id<T>where
&'a mut T: IntoIterator,
impl<'a, T: ?Sized + IsMutable> IntoIterator for &'a mut Id<T>where
&'a mut T: IntoIterator,
source§impl<T: ?Sized + IdIntoIterator> IntoIterator for Id<T>
impl<T: ?Sized + IdIntoIterator> IntoIterator for Id<T>
source§impl<T: Ord + ?Sized> Ord for Id<T>
impl<T: Ord + ?Sized> Ord for Id<T>
source§impl<T: PartialOrd + ?Sized> PartialOrd for Id<T>
impl<T: PartialOrd + ?Sized> PartialOrd for Id<T>
source§fn le(&self, other: &Self) -> bool
fn le(&self, other: &Self) -> bool
self
and other
) and is used by the <=
operator. Read moresource§impl<T: Read + ?Sized + IsMutable> Read for Id<T>
impl<T: Read + ?Sized + IsMutable> Read for Id<T>
source§fn read(&mut self, buf: &mut [u8]) -> Result<usize>
fn read(&mut self, buf: &mut [u8]) -> Result<usize>
source§fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
read
, except that it reads into a slice of buffers. Read moresource§fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize>
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize>
buf
. Read moresource§fn read_to_string(&mut self, buf: &mut String) -> Result<usize>
fn read_to_string(&mut self, buf: &mut String) -> Result<usize>
buf
. Read moresource§fn read_exact(&mut self, buf: &mut [u8]) -> Result<()>
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()>
buf
. Read moresource§fn is_read_vectored(&self) -> bool
fn is_read_vectored(&self) -> bool
can_vector
)source§fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<(), Error>
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<(), Error>
read_buf
)source§fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<(), Error>
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<(), Error>
read_buf
)cursor
. Read more1.0.0 · source§fn by_ref(&mut self) -> &mut Selfwhere
Self: Sized,
fn by_ref(&mut self) -> &mut Selfwhere
Self: Sized,
Read
. Read moresource§impl<T: Seek + ?Sized + IsMutable> Seek for Id<T>
impl<T: Seek + ?Sized + IsMutable> Seek for Id<T>
source§fn seek(&mut self, pos: SeekFrom) -> Result<u64>
fn seek(&mut self, pos: SeekFrom) -> Result<u64>
source§fn stream_position(&mut self) -> Result<u64>
fn stream_position(&mut self) -> Result<u64>
1.55.0 · source§fn rewind(&mut self) -> Result<(), Error>
fn rewind(&mut self) -> Result<(), Error>
source§impl<T: Write + ?Sized + IsMutable> Write for Id<T>
impl<T: Write + ?Sized + IsMutable> Write for Id<T>
source§fn write(&mut self, buf: &[u8]) -> Result<usize>
fn write(&mut self, buf: &[u8]) -> Result<usize>
source§fn flush(&mut self) -> Result<()>
fn flush(&mut self) -> Result<()>
source§fn write_all(&mut self, buf: &[u8]) -> Result<()>
fn write_all(&mut self, buf: &[u8]) -> Result<()>
source§fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()>
fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()>
source§fn is_write_vectored(&self) -> bool
fn is_write_vectored(&self) -> bool
can_vector
)impl<T: Eq + ?Sized> Eq for Id<T>
impl<T: ?Sized + RefUnwindSafe> RefUnwindSafe for Id<T>
impl<T: ?Sized + ClassType + Send> Send for Id<T>where
T::Mutability: IdSendSyncHelper<T>,
EquivalentType<T>: Send,
Id<T>
is always Send
if T
is Send + Sync
.
Additionally, for mutable types, T
doesn’t have to be Sync
(only
requires T: Send
), since it has unique access to the object.
impl<T: ?Sized + ClassType + Sync> Sync for Id<T>where
T::Mutability: IdSendSyncHelper<T>,
EquivalentType<T>: Sync,
Id<T>
is always Sync
if T
is Send + Sync
.
Additionally, for mutable types, T
doesn’t have to be Send
(only
requires T: Sync
), since it has unique access to the object.