Skip to content

Commit

Permalink
Remove StrongPtr and WeakPtr
Browse files Browse the repository at this point in the history
We want to encourage users to use Id and WeakId, because they provide much stronger guarantees. The other types can in most cases be emulated as:
type StrongPtr = Option<Id<Object, Shared>>
type WeakPtr = WeakId<Object>

And for the cases where they can't, their implementation is pretty simple anyhow, so advanced users can just use objc2_sys to implement the functionality they need themselves.
  • Loading branch information
madsmtm committed Sep 9, 2021
1 parent 470a4b7 commit e08e87d
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 257 deletions.
26 changes: 14 additions & 12 deletions objc2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,33 @@ unsafe {

The utilities of the `rc` module provide ARC-like semantics for working with
Objective-C's reference counted objects in Rust.
A `StrongPtr` retains an object and releases the object when dropped.
A `WeakPtr` will not retain the object, but can be upgraded to a `StrongPtr`
and safely fails if the object has been deallocated.

An `Id` retains an object and releases the object when dropped.
A `WeakId` will not retain the object, but can be upgraded to an `Id` and
safely fails if the object has been deallocated.

```rust , no_run
use objc2::{class, msg_send};
use objc2::rc::{autoreleasepool, StrongPtr};
use objc2::rc::{autoreleasepool, Id, Shared, WeakId};
use objc2::runtime::Object;

// StrongPtr will release the object when dropped
let obj = unsafe {
StrongPtr::new(msg_send![class!(NSObject), new])
// Id will release the object when dropped
let obj: Id<Object, Shared> = unsafe {
Id::new(msg_send![class!(NSObject), new])
};

// Cloning retains the object an additional time
let cloned = obj.clone();
autoreleasepool(|_| {
// Autorelease consumes the StrongPtr, but won't
autoreleasepool(|pool| {
// Autorelease consumes the Id, but won't
// actually release until the end of an autoreleasepool
cloned.autorelease();
let obj_ref: &Object = cloned.autorelease(pool);
});

// Weak references won't retain the object
let weak = obj.weak();
let weak = WeakId::new(&obj);
drop(obj);
assert!(weak.load().is_null());
assert!(weak.load().is_none());
```

## Declaring classes
Expand Down
14 changes: 8 additions & 6 deletions objc2/examples/introspection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use objc2::rc::StrongPtr;
use core::ptr::NonNull;

use objc2::rc::{Id, Owned};
use objc2::runtime::{Class, Object};
use objc2::{class, msg_send, sel, Encode};

Expand All @@ -14,15 +16,15 @@ fn main() {
}

// Allocate an instance
let obj = unsafe {
let obj: Id<Object, Owned> = unsafe {
let obj: *mut Object = msg_send![cls, alloc];
let obj: *mut Object = msg_send![obj, init];
StrongPtr::new(obj)
let obj: NonNull<Object> = msg_send![obj, init];
Id::new(obj)
};
println!("NSObject address: {:p}", obj);

// Access an ivar of the object
let isa: *const Class = unsafe { *(**obj).get_ivar("isa") };
let isa: *const Class = unsafe { *obj.get_ivar("isa") };
println!("NSObject isa: {:?}", isa);

// Inspect a method of the class
Expand All @@ -33,6 +35,6 @@ fn main() {
assert!(*hash_return == usize::ENCODING);

// Invoke a method on the object
let hash: usize = unsafe { msg_send![*obj, hash] };
let hash: usize = unsafe { msg_send![obj, hash] };
println!("NSObject hash: {}", hash);
}
9 changes: 6 additions & 3 deletions objc2/src/exception.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::rc::StrongPtr;
use core::ptr::NonNull;

use crate::rc::Id;
use crate::runtime::Object;
use objc2_exception::{r#try, Exception};

// Comment copied from `objc2_exception`

Expand All @@ -18,6 +21,6 @@ use crate::runtime::Object;
/// undefined behaviour until `C-unwind` is stabilized, see [RFC-2945].
///
/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
pub unsafe fn catch_exception<R>(closure: impl FnOnce() -> R) -> Result<R, StrongPtr> {
objc2_exception::r#try(closure).map_err(|exception| StrongPtr::new(exception as *mut Object))
pub unsafe fn catch_exception<R>(closure: impl FnOnce() -> R) -> Result<R, Id<Exception>> {
r#try(closure).map_err(|e| Id::new(NonNull::new(e).unwrap()))
}
16 changes: 16 additions & 0 deletions objc2/src/rc/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,13 +517,29 @@ mod tests {
use core::ptr::NonNull;

use super::{Id, Shared};
use crate::rc::autoreleasepool;
use crate::runtime::Object;
use crate::{class, msg_send};

fn retain_count(obj: &Object) -> usize {
unsafe { msg_send![obj, retainCount] }
}

#[test]
fn test_autorelease() {
let obj: Id<Object, Shared> = unsafe { Id::new(msg_send![class!(NSObject), new]) };

let cloned = obj.clone();

autoreleasepool(|pool| {
let _ref = obj.autorelease(pool);
assert_eq!(retain_count(&*cloned), 2);
});

// make sure that the autoreleased value has been released
assert_eq!(retain_count(&*cloned), 1);
}

#[test]
fn test_clone() {
let cls = class!(NSObject);
Expand Down
107 changes: 3 additions & 104 deletions objc2/src/rc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,8 @@
//! The utilities of the `rc` module provide ARC-like semantics for working
//! with Objective-C's reference counted objects in Rust.
//!
//! A `StrongPtr` retains an object and releases the object when dropped.
//! A `WeakPtr` will not retain the object, but can be upgraded to a `StrongPtr`
//! and safely fails if the object has been deallocated.
//!
//! These utilities are not intended to provide a fully safe interface, but can be
//! useful when writing higher-level Rust wrappers for Objective-C code.
//!
//! A smart pointer version of this is provided with the `Id` struct.
//! To ensure that Objective-C objects are retained and released
//! at the proper times.
//! A smart pointer [`Id`] is provided to ensure that Objective-C objects are
//! retained and released at the proper times.
//!
//! To enforce aliasing rules, an `Id` can be either owned or shared; if it is
//! owned, meaning the `Id` is the only reference to the object, it can be
Expand All @@ -33,54 +25,22 @@
//! [mem-mgmt]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
//! [mem-cf]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html
//! [mem-debug]: https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
//!
//! # Example
//!
//! ``` no_run
//! # use objc2::{class, msg_send};
//! # use objc2::rc::{autoreleasepool, StrongPtr};
//! // StrongPtr will release the object when dropped
//! let obj = unsafe {
//! StrongPtr::new(msg_send![class!(NSObject), new])
//! };
//!
//! // Cloning retains the object an additional time
//! let cloned = obj.clone();
//! autoreleasepool(|_| {
//! // Autorelease consumes the StrongPtr, but won't
//! // actually release until the end of an autoreleasepool
//! cloned.autorelease();
//! });
//!
//! // Weak references won't retain the object
//! let weak = obj.weak();
//! drop(obj);
//! assert!(weak.load().is_null());
//! ```
mod autorelease;
mod id;
mod ownership;
mod strong;
mod weak;
mod weak_id;

pub use self::autorelease::{autoreleasepool, AutoreleasePool, AutoreleaseSafe};
pub use self::id::{Id, ShareId};
pub use self::ownership::{Owned, Ownership, Shared};
pub use self::strong::StrongPtr;
pub use self::weak::WeakPtr;
pub use self::weak_id::WeakId;

// These tests use NSObject, which isn't present for GNUstep
#[cfg(all(test, target_vendor = "apple"))]
#[cfg(test)]
mod tests {
use core::mem::size_of;

use super::autoreleasepool;
use super::StrongPtr;
use super::{Id, Owned, Shared, WeakId};
use crate::runtime::Object;

pub struct TestType {
_data: [u8; 0], // TODO: `UnsafeCell`?
Expand All @@ -104,65 +64,4 @@ mod tests {
size_of::<*const ()>()
);
}

#[test]
fn test_strong_clone() {
fn retain_count(obj: *mut Object) -> usize {
unsafe { msg_send![obj, retainCount] }
}

let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
assert!(retain_count(*obj) == 1);

let cloned = obj.clone();
assert!(retain_count(*cloned) == 2);
assert!(retain_count(*obj) == 2);

drop(obj);
assert!(retain_count(*cloned) == 1);
}

#[test]
fn test_weak() {
let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
let weak = obj.weak();

let strong = weak.load();
assert!(*strong == *obj);
drop(strong);

drop(obj);
assert!(weak.load().is_null());
}

#[test]
fn test_weak_copy() {
let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
let weak = obj.weak();

let weak2 = weak.clone();

let strong = weak.load();
let strong2 = weak2.load();
assert!(*strong == *obj);
assert!(*strong2 == *obj);
}

#[test]
fn test_autorelease() {
let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };

fn retain_count(obj: *mut Object) -> usize {
unsafe { msg_send![obj, retainCount] }
}
let cloned = obj.clone();

autoreleasepool(|_| {
obj.autorelease();
assert!(retain_count(*cloned) == 2);
});

// make sure that the autoreleased value has been released
assert!(retain_count(*cloned) == 1);
}
}
77 changes: 0 additions & 77 deletions objc2/src/rc/strong.rs

This file was deleted.

Loading

0 comments on commit e08e87d

Please sign in to comment.