Skip to content

Commit 7273966

Browse files
committed
Add id_msg_send to help with following memory management rules
1 parent 419086c commit 7273966

File tree

5 files changed

+90
-27
lines changed

5 files changed

+90
-27
lines changed

objc2-foundation/src/data.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ use core::slice::{self, SliceIndex};
55
use core::{ffi::c_void, ptr::NonNull};
66

77
use super::{INSCopying, INSMutableCopying, INSObject, NSRange};
8-
use objc2::msg_send;
98
use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared};
9+
use objc2::{id_msg_send, msg_send};
1010

11-
pub unsafe trait INSData: INSObject {
11+
pub unsafe trait INSData: INSObject + Sized {
1212
type Ownership: Ownership;
1313

1414
unsafe_def_fn!(fn new -> Self::Ownership);
@@ -35,13 +35,12 @@ pub unsafe trait INSData: INSObject {
3535
let cls = Self::class();
3636
let bytes_ptr = bytes.as_ptr() as *const c_void;
3737
unsafe {
38-
let obj: *mut Self = msg_send![cls, alloc];
39-
let obj: *mut Self = msg_send![
38+
let obj: Id<Self, Self::Ownership> = id_msg_send![cls, alloc];
39+
id_msg_send![
4040
obj,
4141
initWithBytes: bytes_ptr,
4242
length: bytes.len(),
43-
];
44-
Id::new(NonNull::new_unchecked(obj))
43+
]
4544
}
4645
}
4746

@@ -80,14 +79,13 @@ pub unsafe trait INSData: INSObject {
8079
let mut bytes = ManuallyDrop::new(bytes);
8180

8281
unsafe {
83-
let obj: *mut Self = msg_send![cls, alloc];
84-
let obj: *mut Self = msg_send![
82+
let obj: Id<Self, Self::Ownership> = id_msg_send![cls, alloc];
83+
id_msg_send![
8584
obj,
8685
initWithBytesNoCopy: bytes.as_mut_ptr() as *mut c_void,
8786
length: bytes.len(),
8887
deallocator: dealloc,
89-
];
90-
Id::new(NonNull::new_unchecked(obj))
88+
]
9189
}
9290
}
9391
}

objc2-foundation/tests/objc_id_retain_autoreleased.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use std::ffi::c_void;
2-
use std::ptr::NonNull;
32

4-
use objc2::msg_send;
53
use objc2::rc::{autoreleasepool, Id, Shared};
4+
use objc2::{id_msg_send, msg_send};
65
use objc2_foundation::{INSObject, NSData};
76

87
fn retain_count(obj: &NSData) -> usize {
@@ -12,14 +11,11 @@ fn retain_count(obj: &NSData) -> usize {
1211
fn create_data(bytes: &[u8]) -> Id<NSData, Shared> {
1312
let bytes_ptr = bytes.as_ptr() as *const c_void;
1413
unsafe {
15-
// All code between the `msg_send!` and the `retain_autoreleased` must
16-
// be able to be optimized away for this to work.
17-
let obj: *mut NSData = msg_send![
14+
id_msg_send![
1815
NSData::class(),
1916
dataWithBytes: bytes_ptr,
2017
length: bytes.len(),
21-
];
22-
Id::retain_autoreleased(NonNull::new_unchecked(obj))
18+
]
2319
}
2420
}
2521

objc2/examples/introspection.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
use core::ptr::NonNull;
2-
31
use objc2::rc::{Id, Owned};
42
use objc2::runtime::{Class, Object};
5-
use objc2::{class, msg_send};
3+
use objc2::{class, id_msg_send, msg_send};
64
#[cfg(feature = "malloc")]
75
use objc2::{sel, Encode};
86

@@ -22,9 +20,8 @@ fn main() {
2220

2321
// Allocate an instance
2422
let obj: Id<Object, Owned> = unsafe {
25-
let obj: *mut Object = msg_send![cls, alloc];
26-
let obj: NonNull<Object> = msg_send![obj, init];
27-
Id::new(obj)
23+
let obj: Id<Object, Owned> = id_msg_send![cls, alloc];
24+
id_msg_send![obj, init]
2825
};
2926
println!("NSObject address: {:p}", obj);
3027

objc2/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,14 @@
3030
#![cfg_attr(apple, doc = "```")]
3131
#![cfg_attr(not(apple), doc = "```no_run")]
3232
//! use core::ptr::NonNull;
33-
//! use objc2::{class, msg_send};
33+
//! use objc2::{class, id_msg_send, msg_send};
3434
//! use objc2::ffi::NSUInteger;
3535
//! use objc2::rc::{Id, Owned};
3636
//! use objc2::runtime::{Bool, Object};
3737
//!
3838
//! // Creation
3939
//! let cls = class!(NSObject);
40-
//! let obj: *mut Object = unsafe { msg_send![cls, new] };
41-
//! let obj = NonNull::new(obj).expect("Failed allocating object");
42-
//! let obj: Id<Object, Owned> = unsafe { Id::new(obj) };
40+
//! let obj: Id<Object, Owned> = unsafe { id_msg_send![cls, new] }; // .expect("Failed allocating object")
4341
//!
4442
//! // Usage
4543
//! let hash: NSUInteger = unsafe { msg_send![obj, hash] };
@@ -150,6 +148,8 @@ pub use crate::message::{Message, MessageArguments, MessageError, MessageReceive
150148
pub use crate::cache::CachedClass as __CachedClass;
151149
pub use crate::cache::CachedSel as __CachedSel;
152150

151+
pub use crate::macros::__starts_with_str;
152+
153153
#[macro_use]
154154
mod macros;
155155

objc2/src/macros.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,75 @@ macro_rules! msg_send {
172172
result
173173
});
174174
}
175+
176+
/// TODO
177+
#[macro_export]
178+
macro_rules! id_msg_send {
179+
($obj:expr, $name:ident) => ({
180+
const NAME: &[u8] = stringify!($name).as_bytes();
181+
$crate::id_msg_send!(@ $obj, NAME, $name)
182+
});
183+
($obj:expr, $($name:ident: $arg:expr),+ $(,)?) => ({
184+
const NAME: &[u8] = concat!($(stringify!($name), ':'),+).as_bytes();
185+
$crate::id_msg_send!(@ $obj, NAME, $($name: $arg),+)
186+
});
187+
(@ $obj:expr, $name:ident, $($sel:tt)+) => {{
188+
const IS_INIT: bool = $crate::__starts_with_str($name, b"init");
189+
const IS_RETAINED: bool = {
190+
$crate::__starts_with_str($name, b"alloc")
191+
|| $crate::__starts_with_str($name, b"new")
192+
|| $crate::__starts_with_str($name, b"copy")
193+
|| $crate::__starts_with_str($name, b"mutableCopy")
194+
|| $crate::__starts_with_str($name, b"init")
195+
};
196+
197+
::std::println!("IS_INIT: {}", IS_INIT);
198+
::std::println!("IS_RETAINED: {}", IS_RETAINED);
199+
200+
let result = if IS_INIT {
201+
// TODO: Ensure `obj` is Id here
202+
let obj = ::core::mem::ManuallyDrop::new($obj);
203+
$crate::msg_send![obj, $($sel)+]
204+
} else {
205+
$crate::msg_send![$obj, $($sel)+]
206+
};
207+
if IS_RETAINED {
208+
$crate::rc::Id::new(result)
209+
} else {
210+
// All code between the `msg_send!` and the `retain_autoreleased` must
211+
// be able to be optimized away for this to work.
212+
$crate::rc::Id::retain_autoreleased(result)
213+
}
214+
}};
215+
}
216+
217+
#[doc(hidden)]
218+
pub const fn __starts_with_str(haystack: &[u8], needle: &[u8]) -> bool {
219+
if needle.len() > haystack.len() {
220+
return false;
221+
}
222+
let mut i = 0;
223+
while i < needle.len() {
224+
if needle[i] != haystack[i] {
225+
return false;
226+
}
227+
i += 1;
228+
}
229+
true
230+
}
231+
232+
#[cfg(test)]
233+
mod tests {
234+
use super::*;
235+
236+
#[test]
237+
fn test_starts_with_str() {
238+
assert!(__starts_with_str(b"abcdef", b"abc"));
239+
assert!(__starts_with_str(b"a", b""));
240+
assert!(__starts_with_str(b"", b""));
241+
242+
assert!(!__starts_with_str(b"abcdef", b"def"));
243+
assert!(!__starts_with_str(b"abcdef", b"abb"));
244+
assert!(!__starts_with_str(b"", b"a"));
245+
}
246+
}

0 commit comments

Comments
 (0)