Skip to content

Commit 9f393ff

Browse files
committed
Add MainThreadMarker::alloc
1 parent a33e048 commit 9f393ff

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

crates/icrate/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2828
- `NSDictionary::get_retained`
2929
- `NSDictionary::keys_retained`
3030
- `NSDictionary::values_retained`
31+
* Added `MainThreadMarker::alloc` for allocating objects that need to be so on
32+
the main thread.
3133

3234
### Changed
3335
* **BREAKING**: Renamed the `from_slice` method on `NSArray`, `NSSet`,

crates/icrate/src/Foundation/additions/thread.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use core::panic::{RefUnwindSafe, UnwindSafe};
77
use crate::common::*;
88
use crate::Foundation::NSThread;
99

10+
use objc2::msg_send_id;
11+
1012
unsafe impl Send for NSThread {}
1113
unsafe impl Sync for NSThread {}
1214

@@ -120,6 +122,43 @@ impl MainThreadMarker {
120122
Self { _priv: PhantomData }
121123
}
122124

125+
/// Allocate a new instance of the specified class on the main thread.
126+
///
127+
/// This is essentially the same as [`ClassType::alloc`], the difference
128+
/// being that it is also callable with classes that can only be used on
129+
/// the main thread.
130+
///
131+
///
132+
/// # Example
133+
///
134+
/// Create an object on the main thread.
135+
///
136+
/// ```
137+
/// use icrate::Foundation::MainThreadMarker;
138+
/// # use icrate::Foundation::NSObject as SomeClass;
139+
/// # #[cfg(for_example)]
140+
/// use icrate::SomeFramework::SomeClass;
141+
/// use objc2::rc::Id;
142+
/// use objc2::msg_send_id;
143+
///
144+
/// # let mtm = unsafe { MainThreadMarker::new_unchecked() };
145+
/// # #[cfg(doctests_not_always_run_on_main_thread)]
146+
/// let mtm = MainThreadMarker::new().expect("must be on the main thread");
147+
///
148+
/// // _All_ objects are safe to allocate on the main thread!
149+
/// let obj = mtm.alloc::<SomeClass>();
150+
///
151+
/// // Though more knowledge is required for safe initialization
152+
/// let obj: Id<SomeClass> = unsafe { msg_send_id![obj, init] };
153+
/// ```
154+
#[inline]
155+
pub fn alloc<T: ClassType>(self) -> Option<Allocated<T>> {
156+
// SAFETY: Same as `ClassType::alloc`, with the addition that since we
157+
// take `self: MainThreadMarker`, the `IsAllocableAnyThread` bound is
158+
// not required.
159+
unsafe { msg_send_id![T::class(), alloc] }
160+
}
161+
123162
/// Submit the given closure to the runloop on the main thread.
124163
///
125164
/// If the current thread is the main thread, this simply runs the

crates/objc2/src/class_type.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ pub unsafe trait ClassType: Message {
168168
/// The return value can be used directly inside [`msg_send_id!`] to
169169
/// initialize the object.
170170
///
171+
/// For classes that are only usable on the main thread, you can use
172+
/// `MainThreadMarker::alloc` instead.
173+
///
171174
/// [`msg_send_id!`]: crate::msg_send_id
172175
//
173176
// Note: We could have placed this on `mutability::IsAllocableAnyThread`,
@@ -185,6 +188,8 @@ pub unsafe trait ClassType: Message {
185188
// - The object is safe to `dealloc` on the current thread (due to the
186189
// `IsAllocableAnyThread` bound which guarantees it is not
187190
// `MainThreadOnly`).
191+
//
192+
// See also `MainThreadMarker::alloc`.
188193
unsafe { msg_send_id![Self::class(), alloc] }
189194
}
190195

0 commit comments

Comments
 (0)