Skip to content

Commit 2bbeb56

Browse files
committed
Add msg_send_id to help with following memory management rules
1 parent b66594d commit 2bbeb56

File tree

3 files changed

+78
-6
lines changed

3 files changed

+78
-6
lines changed

objc2/examples/introspection.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use objc2::rc::{Id, Owned};
22
use objc2::runtime::{Class, Object};
3-
use objc2::{class, msg_send};
3+
use objc2::{class, msg_send, msg_send_id};
44
#[cfg(feature = "malloc")]
55
use objc2::{sel, Encode};
66

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

2121
// Allocate an instance
2222
let obj: Id<Object, Owned> = unsafe {
23-
let obj: *mut Object = msg_send![cls, alloc];
24-
let obj: *mut Object = msg_send![obj, init];
25-
Id::new(obj).unwrap()
23+
let obj: Id<Object, Owned> = msg_send_id![cls, alloc].unwrap();
24+
msg_send_id![obj, init].unwrap()
2625
};
2726
println!("NSObject address: {:p}", obj);
2827

objc2/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@
3636
//!
3737
//! // Creation
3838
//! let cls = class!(NSObject);
39-
//! let obj: *mut Object = unsafe { msg_send![cls, new] };
4039
//! let obj: Id<Object, Owned> = unsafe {
41-
//! Id::new(obj).expect("Failed allocating object")
40+
//! msg_send_id![cls, new].expect("Failed allocating object")
4241
//! };
4342
//!
4443
//! // Usage
@@ -152,6 +151,8 @@ pub use crate::message::{Message, MessageArguments, MessageError, MessageReceive
152151
pub use crate::cache::CachedClass as __CachedClass;
153152
pub use crate::cache::CachedSel as __CachedSel;
154153

154+
pub use crate::macros::__starts_with_str;
155+
155156
#[macro_use]
156157
mod macros;
157158

objc2/src/macros.rs

+72
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! msg_send_id {
179+
($obj:expr, $name:ident) => ({
180+
const NAME: &[u8] = stringify!($name).as_bytes();
181+
$crate::msg_send_id!(@ $obj, NAME, $name)
182+
});
183+
($obj:expr, $($name:ident: $arg:expr),+ $(,)?) => ({
184+
const NAME: &[u8] = concat!($(stringify!($name), ':'),+).as_bytes();
185+
$crate::msg_send_id!(@ $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)