Function objc2::rc::autoreleasepool

source ·
pub fn autoreleasepool<T, F>(f: F) -> T
where for<'pool> F: AutoreleaseSafe + FnOnce(AutoreleasePool<'pool>) -> T,
Expand description

Execute f in the context of a new autorelease pool. The pool is drained after the execution of f completes.

This corresponds to @autoreleasepool blocks in Objective-C and Swift.

The pool is passed as a parameter to the closure to give you a lifetime parameter that autoreleased objects can refer to.

Note that this is mostly useful for preventing leaks (as any Objective-C method may autorelease internally - see also autoreleasepool_leaking). If implementing an interface to an object, you should try to return retained pointers with msg_send_id! wherever you can instead, since it is usually more efficient, and having to use this function can be quite cumbersome for users.

§Restrictions

The given parameter must not be used in an inner autoreleasepool - doing so will panic with debug assertions enabled, and be a compile error in a future release.

Note that this means that this function is currently unsound, since it doesn’t disallow wrong usage in all cases. Enabling the assertions in release mode would be prohibitively expensive though, so this is the least-bad solution.

You can try to compile your crate with the "unstable-autoreleasesafe" crate feature enabled on nightly Rust - if your crate compiles with that, its autoreleasepool usage is guaranteed to be correct.

§Examples

Basic usage:

use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::NSObject;

autoreleasepool(|pool| {
    // Create `obj` and autorelease it to the pool
    let obj = Retained::autorelease(NSObject::new(), pool);
    // We now have a reference that we can freely use
    println!("{obj:?}");
    // `obj` is deallocated when the pool ends
});
// And is no longer usable outside the closure

Fails to compile because obj does not live long enough for us to take it out of the pool:

use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::NSObject;

let obj = autoreleasepool(|pool| {
    Retained::autorelease(NSObject::new(), pool)
});

Fails to compile with the "unstable-autoreleasesafe" feature enabled, or panics with debug assertions enabled, because we tried to pass an outer pool to an inner pool:

use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::NSObject;

autoreleasepool(|outer_pool| {
    let obj = autoreleasepool(|inner_pool| {
        Retained::autorelease(NSObject::new(), outer_pool)
    });
    // `obj` could wrongly be used here because its lifetime was
    // assigned to the outer pool, even though it was released by the
    // inner pool already.
});

It is impossible to extend the lifetime of the pool.

use std::cell::RefCell;
use objc2::rc::{autoreleasepool, AutoreleasePool};

thread_local! {
    static POOL: RefCell<Option<&'static AutoreleasePool<'static>>> = RefCell::new(None);
}

autoreleasepool(|pool| {
    POOL.with(|p| {
        *p.borrow_mut() = Some(Box::leak(Box::new(pool)))
    });
});