@@ -108,16 +108,21 @@ impl Drop for Pool {
108
108
/// use objc2::runtime::NSObject;
109
109
/// use objc2::msg_send;
110
110
///
111
- /// fn needs_lifetime_from_pool<'p>(pool: AutoreleasePool<'p>) -> &'p NSObject {
111
+ /// unsafe fn needs_lifetime_from_pool<'p>(pool: AutoreleasePool<'p>) -> &'p NSObject {
112
112
/// let obj = NSObject::new();
113
113
/// // Do action that returns an autoreleased object
114
114
/// let description: *mut NSObject = unsafe { msg_send![&obj, description] };
115
- /// // Bound the lifetime of the reference to the pool
115
+ /// // Bound the lifetime of the reference to that of the pool.
116
+ /// //
117
+ /// // Note that this only helps ensuring soundness, our function is still
118
+ /// // unsafe because the pool cannot be guaranteed to be the innermost
119
+ /// // pool.
116
120
/// unsafe { pool.ptr_as_ref(description) }
117
121
/// }
118
122
///
119
123
/// autoreleasepool(|pool| {
120
- /// let obj = needs_lifetime_from_pool(pool);
124
+ /// // SAFETY: The given pool is the innermost pool.
125
+ /// let obj = unsafe { needs_lifetime_from_pool(pool) };
121
126
/// println!("{obj:?}");
122
127
/// });
123
128
/// ```
@@ -178,10 +183,8 @@ impl<'pool> AutoreleasePool<'pool> {
178
183
}
179
184
}
180
185
181
- /// This will be removed in a future version.
182
186
#[ inline]
183
- #[ doc( hidden) ]
184
- pub fn __verify_is_inner ( self ) {
187
+ pub ( crate ) fn __verify_is_inner ( self ) {
185
188
#[ cfg( all( debug_assertions, not( feature = "unstable-autoreleasesafe" ) ) ) ]
186
189
if let Some ( pool) = & self . inner {
187
190
POOLS . with ( |c| {
@@ -200,11 +203,24 @@ impl<'pool> AutoreleasePool<'pool> {
200
203
/// objects, since it binds the lifetime of the reference to the pool, and
201
204
/// does some extra checks when debug assertions are enabled.
202
205
///
206
+ /// Note that this is helpful, but not sufficient, for ensuring that the
207
+ /// lifetime of the reference does not exceed the lifetime of the
208
+ /// autorelease pool. When calling this, you must also ensure that the
209
+ /// pool is actually the current innermost pool.
210
+ ///
211
+ ///
212
+ /// # Panics
213
+ ///
214
+ /// If the pool is not the innermost pool, this function may panic when
215
+ /// the `"std"` Cargo feature and debug assertions are enabled.
216
+ ///
203
217
///
204
218
/// # Safety
205
219
///
206
220
/// This is equivalent to `&*ptr`, and shares the unsafety of that, except
207
221
/// the lifetime is bound to the pool instead of being unbounded.
222
+ ///
223
+ /// The pool must be the innermost pool for the lifetime to be correct.
208
224
#[ inline]
209
225
pub unsafe fn ptr_as_ref < T : ?Sized > ( self , ptr : * const T ) -> & ' pool T {
210
226
self . __verify_is_inner ( ) ;
@@ -304,51 +320,66 @@ impl !AutoreleaseSafe for AutoreleasePool<'_> {}
304
320
/// see [Apple's documentation][apple-autorelease] for general information on
305
321
/// when to use those.
306
322
///
307
- /// The pool is passed as a parameter to the closure to give you a lifetime
323
+ /// [ The pool] is passed as a parameter to the closure to give you a lifetime
308
324
/// parameter that autoreleased objects can refer to.
309
325
///
310
326
/// Note that this is mostly useful for preventing leaks (as any Objective-C
311
327
/// method may autorelease internally - see also [`autoreleasepool_leaking`]).
312
328
/// If implementing an interface to an object, you should try to return
313
329
/// retained pointers with [`msg_send_id!`] wherever you can instead, since
314
- /// it is usually more efficient, and having to use this function can be quite
315
- /// cumbersome for users.
330
+ /// it is usually more efficient, safer, and having to use this function can
331
+ /// be quite cumbersome for users.
316
332
///
317
333
/// [apple-autorelease]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
334
+ /// [The pool]: AutoreleasePool
318
335
/// [`msg_send_id!`]: crate::msg_send_id
319
336
///
320
337
///
321
338
/// # Restrictions
322
339
///
323
- /// The given parameter must not be used in an inner `autoreleasepool` - doing
324
- /// so will panic with debug assertions enabled, and be a compile error in a
325
- /// future release .
340
+ /// The given parameter must not be used inside a nested `autoreleasepool`,
341
+ /// since doing so will give the objects that it is used with an incorrect
342
+ /// lifetime bound .
326
343
///
327
- /// Note that this means that **this function is currently unsound**, since it
328
- /// doesn't disallow wrong usage in all cases. Enabling the assertions in
329
- /// release mode would be prohibitively expensive though, so this is the
330
- /// least-bad solution.
344
+ /// You can try to enable the `"unstable-autoreleasesafe"` Cargo feature using
345
+ /// nightly Rust - if your use of this function compiles with that, it is more
346
+ /// likely to be correct, though note that Rust does not have a way to express
347
+ /// the lifetimes involved, so we cannot detect all invalid usage. See issue
348
+ /// [#540] for details.
331
349
///
332
- /// You can try to compile your crate with the `"unstable-autoreleasesafe"`
333
- /// crate feature enabled on nightly Rust - if your crate compiles with that,
334
- /// its autoreleasepool usage is guaranteed to be correct.
350
+ /// [#540]: https://github.com/madsmtm/objc2/issues/540
335
351
///
336
352
///
337
353
/// # Examples
338
354
///
339
- /// Basic usage:
355
+ /// Use an external API, and ensure that the memory that it used is cleaned
356
+ /// up afterwards.
357
+ ///
358
+ /// ```
359
+ /// use objc2::rc::autoreleasepool;
360
+ /// # fn example_function() {}
361
+ /// # #[cfg(for_illustrative_purposes)]
362
+ /// use example_crate::example_function;
363
+ ///
364
+ /// autoreleasepool(|_| {
365
+ /// // Call `example_function` in the context of a pool
366
+ /// example_function();
367
+ /// }); // Memory released into the pool is cleaned up when the scope ends
368
+ /// ```
369
+ ///
370
+ /// Autorelease an object into an autorelease pool:
340
371
///
341
372
/// ```
342
373
/// use objc2::rc::{autoreleasepool, Retained};
343
374
/// use objc2::runtime::NSObject;
344
375
///
345
376
/// autoreleasepool(|pool| {
346
377
/// // Create `obj` and autorelease it to the pool
347
- /// let obj = Retained::autorelease(NSObject::new(), pool);
378
+ /// // SAFETY: The pool is the current innermost pool
379
+ /// let obj = unsafe { Retained::autorelease(NSObject::new(), pool) };
348
380
/// // We now have a reference that we can freely use
349
381
/// println!("{obj:?}");
350
- /// // `obj` is deallocated when the pool ends
351
- /// });
382
+ /// }); // `obj` is deallocated when the pool ends
352
383
/// // And is no longer usable outside the closure
353
384
/// ```
354
385
///
@@ -359,7 +390,7 @@ impl !AutoreleaseSafe for AutoreleasePool<'_> {}
359
390
/// use objc2::rc::{autoreleasepool, Retained};
360
391
/// use objc2::runtime::NSObject;
361
392
///
362
- /// let obj = autoreleasepool(|pool| {
393
+ /// let obj = autoreleasepool(|pool| unsafe {
363
394
/// Retained::autorelease(NSObject::new(), pool)
364
395
/// });
365
396
/// ```
@@ -375,7 +406,8 @@ impl !AutoreleaseSafe for AutoreleasePool<'_> {}
375
406
///
376
407
/// autoreleasepool(|outer_pool| {
377
408
/// let obj = autoreleasepool(|inner_pool| {
378
- /// Retained::autorelease(NSObject::new(), outer_pool)
409
+ /// // SAFETY: NOT safe, the pool is _not_ the innermost pool!
410
+ /// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
379
411
/// });
380
412
/// // `obj` could wrongly be used here because its lifetime was
381
413
/// // assigned to the outer pool, even though it was released by the
@@ -444,7 +476,8 @@ where
444
476
///
445
477
/// autoreleasepool(|outer_pool| {
446
478
/// let obj = autoreleasepool_leaking(|inner_pool| {
447
- /// Retained::autorelease(NSObject::new(), outer_pool)
479
+ /// // SAFETY: The given `outer_pool` is the actual innermost pool.
480
+ /// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
448
481
/// });
449
482
/// // `obj` is still usable here, since the leaking pool doesn't actually
450
483
/// // do anything.
@@ -461,8 +494,8 @@ where
461
494
/// use objc2::rc::{autoreleasepool_leaking, Retained};
462
495
/// use objc2::runtime::NSObject;
463
496
///
464
- /// let obj = autoreleasepool_leaking(|pool| {
465
- /// Retained::autorelease(NSObject::new(), pool)
497
+ /// let obj = autoreleasepool_leaking(|pool| unsafe {
498
+ /// unsafe { Retained::autorelease(NSObject::new(), pool) }
466
499
/// });
467
500
/// ```
468
501
///
@@ -476,7 +509,8 @@ where
476
509
///
477
510
/// autoreleasepool_leaking(|outer_pool| {
478
511
/// let obj = autoreleasepool(|inner_pool| {
479
- /// Retained::autorelease(NSObject::new(), outer_pool)
512
+ /// // SAFETY: NOT safe, the pool is _not_ the innermost pool!
513
+ /// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
480
514
/// });
481
515
/// });
482
516
/// #
@@ -564,7 +598,7 @@ mod tests {
564
598
let mut expected = expected;
565
599
566
600
autoreleasepool ( |pool| {
567
- let _autoreleased = Retained :: autorelease ( obj. 0 , pool) ;
601
+ let _autoreleased = unsafe { Retained :: autorelease ( obj. 0 , pool) } ;
568
602
expected. autorelease += 1 ;
569
603
expected. assert_current ( ) ;
570
604
panic ! ( "unwind" ) ;
0 commit comments