objc2_foundation/
array.rs

1//! Utilities for the `NSArray` and `NSMutableArray` classes.
2use alloc::vec::Vec;
3#[cfg(feature = "NSEnumerator")]
4use core::fmt;
5use core::mem;
6use core::ptr::NonNull;
7
8use objc2::rc::{Retained, RetainedFromIterator};
9use objc2::{msg_send, AnyThread, Message};
10
11#[cfg(feature = "NSEnumerator")]
12use crate::iter;
13use crate::{util, NSArray, NSMutableArray};
14
15/// Convenience creation methods.
16impl<ObjectType: Message> NSArray<ObjectType> {
17    /// Create a new array from a slice of objects.
18    ///
19    /// This is a safe interface to `initWithObjects:count:`.
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use objc2_foundation::{NSArray, ns_string};
25    ///
26    /// let array = NSArray::from_slice(&[
27    ///     ns_string!("abc"),
28    ///     ns_string!("def"),
29    ///     ns_string!("ghi"),
30    /// ]);
31    /// ```
32    #[doc(alias = "initWithObjects:count:")]
33    pub fn from_slice(slice: &[&ObjectType]) -> Retained<Self> {
34        let len = slice.len();
35        let ptr = util::ref_ptr_cast_const(slice.as_ptr());
36        // SAFETY:
37        // - All `ObjectType: Message` use interior mutability, and the array
38        //   extends the lifetime of them internally by retaining them.
39        // - The pointer and length are valid until the method has finished
40        //   executing, at which point the array will have created its own
41        //   internal storage for holding the pointers.
42        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
43    }
44
45    /// Create a new array from a slice of retained objects.
46    ///
47    /// This is a safe interface to `initWithObjects:count:`.
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// use objc2_foundation::{NSArray, NSObject};
53    ///
54    /// let array = NSArray::from_retained_slice(&[
55    ///     NSObject::new(),
56    ///     NSObject::new(),
57    ///     NSObject::new(),
58    /// ]);
59    /// ```
60    #[doc(alias = "initWithObjects:count:")]
61    pub fn from_retained_slice(slice: &[Retained<ObjectType>]) -> Retained<Self> {
62        let len = slice.len();
63        let ptr = util::retained_ptr_cast_const(slice.as_ptr());
64        // SAFETY: Same as `from_slice`, this is just a faster version to
65        // avoid creating a new slice if your elements are already retained.
66        //
67        // Otherwise equivalent to:
68        //     Self::from_slice(&slice.iter().map(|obj| &*obj).collect())
69        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
70    }
71}
72
73/// Convenience creation methods.
74impl<ObjectType: Message> NSMutableArray<ObjectType> {
75    #[doc(alias = "initWithObjects:count:")]
76    pub fn from_slice(slice: &[&ObjectType]) -> Retained<Self> {
77        let len = slice.len();
78        let ptr = util::ref_ptr_cast_const(slice.as_ptr());
79        // SAFETY: Same as `NSArray::from_slice`.
80        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
81    }
82
83    #[doc(alias = "initWithObjects:count:")]
84    pub fn from_retained_slice(slice: &[Retained<ObjectType>]) -> Retained<Self> {
85        let len = slice.len();
86        let ptr = util::retained_ptr_cast_const(slice.as_ptr());
87        // SAFETY: Same as `NSArray::from_retained_slice`
88        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
89    }
90}
91
92/// Direct, unsafe object accessors.
93///
94/// Foundation's collection types store their items in such a way that they
95/// can give out references to their data without having to autorelease it
96/// first, see [the docs][collections-own].
97///
98/// This means that we can more efficiently access the array's objects, but
99/// _only_ if the array isn't mutated via e.g. `NSMutableArray` methods while
100/// doing so - otherwise, we might end up accessing a deallocated object.
101///
102/// [collections-own]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW12
103impl<ObjectType: Message> NSArray<ObjectType> {
104    /// Get a direct reference to one of the array's objects.
105    ///
106    /// Throws an error if the object was not found.
107    ///
108    /// Consider using the [`objectAtIndex`](Self::objectAtIndex) method
109    /// instead, unless you're seeing performance issues from the retaining.
110    ///
111    /// # Safety
112    ///
113    /// The array must not be mutated while the reference is live.
114    #[doc(alias = "objectAtIndex:")]
115    #[inline]
116    pub unsafe fn objectAtIndex_unchecked(&self, index: usize) -> &ObjectType {
117        // SAFETY: Upheld by caller.
118        unsafe { msg_send![self, objectAtIndex: index] }
119    }
120
121    /// A direct reference to the array's first object, if any.
122    ///
123    /// Consider using the [`firstObject`](Self::firstObject) method instead,
124    /// unless you're seeing performance issues from the retaining.
125    ///
126    /// # Safety
127    ///
128    /// The array must not be mutated while the reference is live.
129    #[doc(alias = "firstObject")]
130    #[inline]
131    pub unsafe fn firstObject_unchecked(&self) -> Option<&ObjectType> {
132        // SAFETY: Upheld by caller.
133        unsafe { msg_send![self, firstObject] }
134    }
135
136    /// A direct reference to the array's last object, if any.
137    ///
138    /// Consider using the [`lastObject`](Self::lastObject) method instead,
139    /// unless you're seeing performance issues from the retaining.
140    ///
141    /// # Safety
142    ///
143    /// The array must not be mutated while the reference is live.
144    #[doc(alias = "lastObject")]
145    #[inline]
146    pub unsafe fn lastObject_unchecked(&self) -> Option<&ObjectType> {
147        // SAFETY: Upheld by caller.
148        unsafe { msg_send![self, lastObject] }
149    }
150
151    /// A vector containing direct references to the array's objects.
152    ///
153    /// Consider using the [`to_vec`](Self::to_vec) method instead, unless
154    /// you're seeing performance issues from the retaining.
155    ///
156    /// # Safety
157    ///
158    /// The array must not be mutated while the returned references are alive.
159    #[doc(alias = "getObjects:")]
160    pub unsafe fn to_vec_unchecked(&self) -> Vec<&ObjectType> {
161        let len = self.count();
162        let mut vec: Vec<NonNull<ObjectType>> = Vec::with_capacity(len);
163        let ptr: NonNull<NonNull<ObjectType>> = NonNull::new(vec.as_mut_ptr()).unwrap();
164
165        // SAFETY: The buffer is at least the size of the array, as guaranteed
166        // by `Vec::with_capacity`.
167        unsafe {
168            #[allow(deprecated)]
169            self.getObjects(ptr)
170        };
171
172        // SAFETY: The elements were just initialized by `getObjects:`.
173        //
174        // Note: We set the length _after_ we've copied the elements, so that
175        // if `getObjects:` unwinds, we don't end up deallocating
176        // uninitialized elements.
177        unsafe { vec.set_len(len) };
178
179        // SAFETY: `NonNull<ObjectType>` has the same layout as `&ObjectType`,
180        // and the lifetime is bound to the array, and caller upholds that the
181        // array isn't mutated.
182        unsafe { mem::transmute::<Vec<NonNull<ObjectType>>, Vec<&ObjectType>>(vec) }
183    }
184
185    /// Iterate over the array without retaining the elements.
186    ///
187    /// Consider using the [`iter`](Self::iter) method instead, unless you're
188    /// seeing performance issues from the retaining.
189    ///
190    /// # Safety
191    ///
192    /// The array must not be mutated for the lifetime of the iterator or for
193    /// the lifetime of the elements the iterator returns.
194    #[cfg(feature = "NSEnumerator")]
195    #[doc(alias = "objectEnumerator")]
196    #[inline]
197    pub unsafe fn iter_unchecked(&self) -> IterUnchecked<'_, ObjectType> {
198        IterUnchecked(iter::IterUnchecked::new(self))
199    }
200}
201
202/// Various accessor methods.
203impl<ObjectType: Message> NSArray<ObjectType> {
204    /// The amount of elements in the array.
205    #[doc(alias = "count")]
206    #[inline]
207    pub fn len(&self) -> usize {
208        self.count()
209    }
210
211    /// Whether the array is empty or not.
212    #[inline]
213    pub fn is_empty(&self) -> bool {
214        self.len() == 0
215    }
216
217    /// Convert the array to a `Vec` of the array's objects.
218    #[doc(alias = "getObjects:")]
219    pub fn to_vec(&self) -> Vec<Retained<ObjectType>> {
220        // SAFETY: We retain the elements below, so we know that the array
221        // isn't mutated while the references are alive.
222        //
223        // Note that this is _technically_ wrong; the user _could_ have
224        // implemented a `retain` method that mutates the array. We're going
225        // to rule this out though, as that's basically never going to happen,
226        // and will make a lot of other things unsound too.
227        let vec = unsafe { self.to_vec_unchecked() };
228        vec.into_iter().map(ObjectType::retain).collect()
229    }
230
231    /// Iterate over the array's elements.
232    #[cfg(feature = "NSEnumerator")]
233    #[doc(alias = "objectEnumerator")]
234    #[inline]
235    pub fn iter(&self) -> Iter<'_, ObjectType> {
236        Iter(iter::Iter::new(self))
237    }
238
239    /// Returns the objects within the given range.
240    ///
241    /// # Panics
242    ///
243    /// Panics if the range was out of bounds.
244    #[doc(alias = "getObjects:range:")]
245    #[cfg(feature = "NSRange")]
246    pub fn objects_in_range(&self, range: core::ops::Range<usize>) -> Vec<Retained<ObjectType>> {
247        let count = self.count();
248
249        // TODO: Replace this check with catching the thrown NSRangeException
250        if range.end > count {
251            panic!(
252                "range end index {} out of range for array of length {}",
253                range.end, count
254            );
255        }
256
257        let range = crate::NSRange::from(range);
258        let mut vec: Vec<NonNull<ObjectType>> = Vec::with_capacity(range.length);
259        let ptr: NonNull<NonNull<ObjectType>> = NonNull::new(vec.as_mut_ptr()).unwrap();
260
261        // SAFETY: Mostly the same as in `to_vec_unchecked`.
262        unsafe { self.getObjects_range(ptr, range) };
263        unsafe { vec.set_len(range.length) };
264        let vec = unsafe { mem::transmute::<Vec<NonNull<ObjectType>>, Vec<&ObjectType>>(vec) };
265
266        vec.into_iter().map(ObjectType::retain).collect()
267    }
268}
269
270/// Convenience mutation methods.
271impl<ObjectType: Message> NSMutableArray<ObjectType> {
272    /// Insert an object into the array at the given index.
273    ///
274    /// # Panics
275    ///
276    /// Panics if the index is out of bounds.
277    #[doc(alias = "insertObject:atIndex:")]
278    pub fn insert(&self, index: usize, obj: &ObjectType) {
279        // TODO: Replace this check with catching the thrown NSRangeException
280        let len = self.len();
281        if index <= len {
282            self.insertObject_atIndex(obj, index)
283        } else {
284            panic!(
285                "insertion index (is {}) should be <= len (is {})",
286                index, len
287            );
288        }
289    }
290
291    /// Sort the array by the given comparison closure.
292    #[cfg(feature = "NSObjCRuntime")]
293    #[doc(alias = "sortUsingFunction:context:")]
294    pub fn sort_by<F: FnMut(&ObjectType, &ObjectType) -> core::cmp::Ordering>(&self, compare: F) {
295        unsafe extern "C-unwind" fn compare_with_closure<
296            ObjectType,
297            F: FnMut(&ObjectType, &ObjectType) -> core::cmp::Ordering,
298        >(
299            obj1: core::ptr::NonNull<ObjectType>,
300            obj2: core::ptr::NonNull<ObjectType>,
301            context: *mut core::ffi::c_void,
302        ) -> isize {
303            let context: *mut F = context.cast();
304            // Bring back a reference to the closure.
305            // Guaranteed to be unique, we gave `sortUsingFunction` unique
306            // ownership, and that method only runs one function at a time.
307            let closure: &mut F = unsafe { context.as_mut().unwrap_unchecked() };
308
309            // SAFETY: The objects are guaranteed to be valid
310            let (obj1, obj2) = unsafe { (obj1.as_ref(), obj2.as_ref()) };
311
312            crate::NSComparisonResult::from((*closure)(obj1, obj2)) as _
313        }
314
315        // Create function pointer
316        let f: unsafe extern "C-unwind" fn(_, _, _) -> _ = compare_with_closure::<ObjectType, F>;
317
318        // Grab a type-erased pointer to the closure (a pointer to stack).
319        let mut closure = compare;
320        let context: *mut F = &mut closure;
321
322        unsafe { self.sortUsingFunction_context(f, context.cast()) };
323        // Keep the closure alive until the function has run.
324        drop(closure);
325    }
326}
327
328#[cfg(feature = "NSEnumerator")]
329unsafe impl<ObjectType: Message> iter::FastEnumerationHelper for NSArray<ObjectType> {
330    type Item = ObjectType;
331
332    #[inline]
333    fn maybe_len(&self) -> Option<usize> {
334        Some(self.len())
335    }
336}
337
338#[cfg(feature = "NSEnumerator")]
339unsafe impl<ObjectType: Message> iter::FastEnumerationHelper for NSMutableArray<ObjectType> {
340    type Item = ObjectType;
341
342    #[inline]
343    fn maybe_len(&self) -> Option<usize> {
344        Some(self.len())
345    }
346}
347
348/// An iterator over the items of an array.
349#[derive(Debug)]
350#[cfg(feature = "NSEnumerator")]
351pub struct Iter<'a, ObjectType: Message>(iter::Iter<'a, NSArray<ObjectType>>);
352
353#[cfg(feature = "NSEnumerator")]
354__impl_iter! {
355    impl<'a, ObjectType: Message> Iterator<Item = Retained<ObjectType>> for Iter<'a, ObjectType> { ... }
356}
357
358/// An iterator over unretained items of an array.
359///
360/// # Safety
361///
362/// The array must not be mutated while this is alive.
363#[derive(Debug)]
364#[cfg(feature = "NSEnumerator")]
365pub struct IterUnchecked<'a, ObjectType: Message>(iter::IterUnchecked<'a, NSArray<ObjectType>>);
366
367#[cfg(feature = "NSEnumerator")]
368__impl_iter! {
369    impl<'a, ObjectType: Message> Iterator<Item = &'a ObjectType> for IterUnchecked<'a, ObjectType> { ... }
370}
371
372/// A retained iterator over the items of an array.
373#[derive(Debug)]
374#[cfg(feature = "NSEnumerator")]
375pub struct IntoIter<ObjectType: Message>(iter::IntoIter<NSArray<ObjectType>>);
376
377#[cfg(feature = "NSEnumerator")]
378__impl_iter! {
379    impl<ObjectType: Message> Iterator<Item = Retained<ObjectType>> for IntoIter<ObjectType> { ... }
380}
381
382#[cfg(feature = "NSEnumerator")]
383__impl_into_iter! {
384    impl<ObjectType: Message> IntoIterator for &NSArray<ObjectType> {
385        type IntoIter = Iter<'_, ObjectType>;
386    }
387
388    impl<ObjectType: Message> IntoIterator for &NSMutableArray<ObjectType> {
389        type IntoIter = Iter<'_, ObjectType>;
390    }
391
392    impl<ObjectType: Message> IntoIterator for Retained<NSArray<ObjectType>> {
393        #[uses(new)]
394        type IntoIter = IntoIter<ObjectType>;
395    }
396
397    impl<ObjectType: Message> IntoIterator for Retained<NSMutableArray<ObjectType>> {
398        #[uses(new_mutable)]
399        type IntoIter = IntoIter<ObjectType>;
400    }
401}
402
403#[cfg(feature = "NSEnumerator")]
404impl<ObjectType: fmt::Debug + Message> fmt::Debug for NSArray<ObjectType> {
405    #[inline]
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        f.debug_list().entries(self).finish()
408    }
409}
410
411#[cfg(feature = "NSEnumerator")]
412impl<ObjectType: fmt::Debug + Message> fmt::Debug for NSMutableArray<ObjectType> {
413    #[inline]
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        fmt::Debug::fmt(&**self, f)
416    }
417}
418
419impl<ObjectType: Message> Extend<Retained<ObjectType>> for &NSMutableArray<ObjectType> {
420    fn extend<I: IntoIterator<Item = Retained<ObjectType>>>(&mut self, iter: I) {
421        iter.into_iter().for_each(move |item| self.addObject(&item));
422    }
423}
424
425impl<'a, ObjectType: Message> Extend<&'a ObjectType> for &NSMutableArray<ObjectType> {
426    fn extend<I: IntoIterator<Item = &'a ObjectType>>(&mut self, iter: I) {
427        iter.into_iter().for_each(move |item| self.addObject(item));
428    }
429}
430
431impl<'a, ObjectType: Message + 'a> RetainedFromIterator<&'a ObjectType> for NSArray<ObjectType> {
432    fn retained_from_iter<I: IntoIterator<Item = &'a ObjectType>>(iter: I) -> Retained<Self> {
433        let vec = Vec::from_iter(iter);
434        Self::from_slice(&vec)
435    }
436}
437
438impl<ObjectType: Message> RetainedFromIterator<Retained<ObjectType>> for NSArray<ObjectType> {
439    fn retained_from_iter<I: IntoIterator<Item = Retained<ObjectType>>>(iter: I) -> Retained<Self> {
440        let vec = Vec::from_iter(iter);
441        Self::from_retained_slice(&vec)
442    }
443}
444
445impl<'a, ObjectType: Message + 'a> RetainedFromIterator<&'a ObjectType>
446    for NSMutableArray<ObjectType>
447{
448    fn retained_from_iter<I: IntoIterator<Item = &'a ObjectType>>(iter: I) -> Retained<Self> {
449        // TODO: Is this, or is using `initWithCapacity` the most optimal?
450        let vec = Vec::from_iter(iter);
451        Self::from_slice(&vec)
452    }
453}
454
455impl<ObjectType: Message> RetainedFromIterator<Retained<ObjectType>>
456    for NSMutableArray<ObjectType>
457{
458    fn retained_from_iter<I: IntoIterator<Item = Retained<ObjectType>>>(iter: I) -> Retained<Self> {
459        // TODO: Is this, or is using `initWithCapacity` the most optimal?
460        let vec = Vec::from_iter(iter);
461        Self::from_retained_slice(&vec)
462    }
463}