Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Vastly improve crate-level documentation in all crates
Browse files Browse the repository at this point in the history
Follow: Crate level docs are thorough and include examples (C-CRATE-DOC)
madsmtm committed Dec 22, 2021
1 parent ae87998 commit 0da814d
Showing 26 changed files with 374 additions and 298 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -5,6 +5,9 @@

# DISCLAIMER! These crates are work in progress, and should not be used in production environments. Use the battle-tested `objc` family instead!

Anyway, thanks for being here, any help testing things out is highly
appreciated!


## Migrating from original crates

10 changes: 9 additions & 1 deletion block-sys/README.md
Original file line number Diff line number Diff line change
@@ -7,6 +7,10 @@

Raw Rust bindings to Apple's C language extension of blocks.

This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
see that for related crates.


## Runtime Support

This library is basically just a raw interface to the aptly specified [Blocks
@@ -16,7 +20,9 @@ several different helper functions), the most important aspect being that the
libraries are named differently, so the linking must take that into account.

The user can choose the desired runtime by using the relevant cargo feature
flags, see the following sections:
flags, see the following sections. Note that if the `objc-sys` crate is
present in the module tree, this should have the same feature flag enabled as
that.


### Apple's [`libclosure`](https://opensource.apple.com/source/libclosure/)
@@ -51,6 +57,8 @@ and is now used in [Swift's `libdispatch`] and [Swift's Foundation] as well.
This can be easily used on many Linux systems with the `libblocksruntime-dev`
package.

Using this runtime probably won't work together with `objc-sys` crate.

[Swift's `libdispatch`]: https://github.com/apple/swift-corelibs-libdispatch/tree/swift-5.5.1-RELEASE/src/BlocksRuntime
[Swift's Foundation]: https://github.com/apple/swift-corelibs-foundation/tree/swift-5.5.1-RELEASE/Sources/BlocksRuntime

5 changes: 3 additions & 2 deletions block-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@
//! sources, but the [ABI specification][ABI] is really the place you should
//! be looking!
//!
//! See also the `README.md` for more info.
//! See also the [`README.md`](https://crates.io/crates/block-sys) for more
//! background information, and for how to configure the desired runtime.
//!
//! [ABI]: https://clang.llvm.org/docs/Block-ABI-Apple.html
@@ -257,7 +258,7 @@ pub struct Block_layout {
/// space on the stack allocated to hold the return value.
pub invoke: Option<unsafe extern "C" fn(block: *mut Block_layout, ...)>,
/// The block's descriptor. The actual type of this is:
/// ```ignore
/// ```pseudo-code
/// match (BLOCK_HAS_COPY_DISPOSE, BLOCK_HAS_SIGNATURE) {
/// (false, false) => Block_descriptor_header,
/// (true, false) => Block_descriptor,
1 change: 1 addition & 0 deletions block2/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Changed
* Changed `global_block!` macro to take an optional semicolon at the end.
* Improved documentation.


## 0.2.0-alpha.2 - 2021-12-22
45 changes: 5 additions & 40 deletions block2/README.md
Original file line number Diff line number Diff line change
@@ -7,45 +7,10 @@

Apple's C language extension of blocks in Rust.

For more information on the specifics of the block implementation, see
Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html
This crate provides functionality for interracting with C blocks, effectively
the C-equivalent of Rust's closures.

## Invoking blocks
See [the docs](https://docs.rs/block2/) for a more thorough overview.

The `Block` struct is used for invoking blocks from Objective-C. For example,
consider this Objective-C function:

```objc
int32_t sum(int32_t (^block)(int32_t, int32_t)) {
return block(5, 8);
}
```
We could write it in Rust as the following:
```rust
use block2::Block;
unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 {
block.call((5, 8))
}
```

Note the extra parentheses in the `call` method, since the arguments must be
passed as a tuple.

## Creating blocks

Creating a block to pass to Objective-C can be done with the `ConcreteBlock`
struct. For example, to create a block that adds two `i32`s, we could write:

```rust
use block2::ConcreteBlock;
let block = ConcreteBlock::new(|a: i32, b: i32| a + b);
let block = block.copy();
assert_eq!(unsafe { block.call((5, 8)) }, 13);
```

It is important to copy your block to the heap (with the `copy` method) before
passing it to Objective-C; this is because our `ConcreteBlock` is only meant
to be copied once, and we can enforce this in Rust, but if Objective-C code
were to copy it twice we could have a double free.
This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
see that for related crates.
59 changes: 40 additions & 19 deletions block2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,63 @@
//! A Rust interface for Objective-C blocks.
//! # Apple's C language extension of blocks
//!
//! For more information on the specifics of the block implementation, see
//! Clang's documentation: <http://clang.llvm.org/docs/Block-ABI-Apple.html>
//! C Blocks are effectively the C-equivalent of Rust's closures, in that they
//! have the ability to capture their environments.
//!
//! # Invoking blocks
//! This crate provides capabilities to create and invoke these blocks, in an
//! ergonomic "Rust-centric" fashion.
//!
//! The `Block` struct is used for invoking blocks from Objective-C. For
//! example, consider this Objective-C function:
//! For more information on the specifics of the block implementation, see the
//! [C language specification][lang] and the [ABI specification][ABI].
//!
//! ``` objc
//! int32_t sum(int32_t (^block)(int32_t, int32_t)) {
//! (Note that while this library can be used separately from Objective-C,
//! they're most commonly used together).
//!
//! ## Invoking blocks
//!
//! The [`Block`] struct is used for invoking blocks from Objective-C. For
//! example, consider this Objective-C function that takes a block as a
//! parameter, executes the block with some arguments, and returns the result:
//!
//! ```objc
//! #include <stdint.h>
//! #include <Block.h>
//! int32_t run_block(int32_t (^block)(int32_t, int32_t)) {
//! return block(5, 8);
//! }
//! ```
//!
//! We could write it in Rust as the following:
//! We could write the equivalent function in Rust like this:
//!
//! ```
//! # use block2::Block;
//! unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 {
//! use block2::Block;
//! unsafe fn run_block(block: &Block<(i32, i32), i32>) -> i32 {
//! block.call((5, 8))
//! }
//! ```
//!
//! Note the extra parentheses in the `call` method, since the arguments must
//! be passed as a tuple.
//!
//! # Creating blocks
//! ## Creating blocks
//!
//! Creating a block to pass to Objective-C can be done with the
//! `ConcreteBlock` struct. For example, to create a block that adds two
//! `i32`s, we could write:
//! [`ConcreteBlock`] struct. For example, to create a block that adds two
//! integers, we could write:
//!
//! ```
//! # use block2::ConcreteBlock;
//! use block2::ConcreteBlock;
//! let block = ConcreteBlock::new(|a: i32, b: i32| a + b);
//! let block = block.copy();
//! assert_eq!(unsafe { block.call((5, 8)) }, 13);
//! ```
//!
//! It is important to copy your block to the heap (with the `copy` method)
//! before passing it to Objective-C; this is because our `ConcreteBlock` is
//! It is important to copy your block to the heap (with the [`copy`] method)
//! before passing it to Objective-C; this is because our [`ConcreteBlock`] is
//! only meant to be copied once, and we can enforce this in Rust, but if
//! Objective-C code were to copy it twice we could have a double free.
//!
//! [`copy`]: ConcreteBlock::copy
//!
//! As an optimization if your block doesn't capture any variables, you can
//! use the [`global_block!`] macro to create a static block:
//!
@@ -52,10 +66,13 @@
//! global_block! {
//! static MY_BLOCK = || -> f32 {
//! 10.0
//! }
//! };
//! };
//! }
//! assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0);
//! ```
//!
//! [lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html
//! [ABI]: http://clang.llvm.org/docs/Block-ABI-Apple.html
#![no_std]
#![warn(elided_lifetimes_in_paths)]
@@ -68,6 +85,10 @@

extern crate std;

#[cfg(doctest)]
#[doc = include_str!("../README.md")]
extern "C" {}

use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop};
9 changes: 7 additions & 2 deletions objc-sys/README.md
Original file line number Diff line number Diff line change
@@ -7,13 +7,18 @@

Raw Rust bindings to the Objective-C runtime and ABI.

This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
see that for related crates.


## Runtime Support

Objective-C has a runtime, different implementations of said runtime exist,
and they act in slightly different ways. By default, Apple platforms link to
Apple's runtime, but if you're using another runtime you must tell it to this
library using feature flags.


### Apple's [`objc4`](https://opensource.apple.com/source/objc4/)

- Feature flag: `apple`.
@@ -144,5 +149,5 @@ Some items (in particular the `objc_msgSend_X` family) have `cfg`s that prevent
their usage on different platforms; these are **semver-stable** in the sense
that they will only get less restrictive, never more.

<sup>1</sup> That said, most of this is created with the help of `bindgen`'s
commandline interface, so huge thanks to them!
<sup>1: That said, most of this is created with the help of `bindgen`'s
commandline interface, so huge thanks to them!</sup>
7 changes: 7 additions & 0 deletions objc-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,9 @@
//! particular `runtime.h`.
//! - GNUStep's `libobjc2` [source code][libobjc2], in particular `runtime.h`.
//!
//! See also the [`README.md`](https://crates.io/crates/objc-sys) for more
//! background information, and for how to configure the desired runtime.
//!
//! [apple]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
//! [libobjc2]: https://github.com/gnustep/libobjc2/tree/v2.1/objc
//! [objc4]: https://opensource.apple.com/source/objc4/objc4-818.2/runtime/
@@ -26,6 +29,10 @@
// See https://github.com/japaric/cty/issues/14.
extern crate std;

#[cfg(doctest)]
#[doc = include_str!("../README.md")]
extern "C" {}

use core::cell::UnsafeCell;
use core::marker::{PhantomData, PhantomPinned};

3 changes: 3 additions & 0 deletions objc2-encode/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
* Implement `Hash` for `Encoding`.

### Changed
* Improved documentation.


## 2.0.0-beta.1 - 2021-12-22

66 changes: 7 additions & 59 deletions objc2-encode/README.md
Original file line number Diff line number Diff line change
@@ -7,65 +7,13 @@

Objective-C type-encoding in Rust.

The Objective-C directive `@encode` encodes types as strings for usage in
various places in the runtime.
This crates provides the equivalent of the Objective-C `@encode` directive,
and functions for comparing these encodings.

This crate provides the `Encoding` type to describe and compare these
type-encodings without memory allocation.
Additionally, it provides traits for annotating types that has an Objective-C
encoding.

Additionally it provides traits for annotating types that has a corresponding
Objective-C encoding, respectively `Encode` for structs and `RefEncode` for
references (and `EncodeArguments` for function arguments).
See [the docs](https://docs.rs/objc2-encode/) for a more thorough overview.

These types are exported under the `objc2` crate as well, so usually you would
just use that.


## Examples

Implementing `Encode` and `RefEncode`:

```rust
use objc2_encode::{Encode, Encoding, RefEncode};

#[repr(C)]
struct MyObject {
a: f32,
b: i16,
}

unsafe impl Encode for MyObject {
const ENCODING: Encoding<'static> = Encoding::Struct(
"MyObject",
&[f32::ENCODING, i16::ENCODING],
);
}

assert!(MyObject::ENCODING.equivalent_to_str("{MyObject=fs}"));

unsafe impl RefEncode for MyObject {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}

assert!(MyObject::ENCODING_REF.equivalent_to_str("^{MyObject=fs}"));
```

An `Encoding` can be compared with an encoding string from the Objective-C
runtime:

```rust
use objc2_encode::Encode;
assert!(i32::ENCODING.equivalent_to_str("i"));
```

`Encoding` implements `Display` as its string representation. This can be
generated conveniently through the `to_string` method:

```rust
use objc2_encode::Encode;
assert_eq!(i32::ENCODING.to_string(), "i");
```

See the [`examples`] folder for more complex usage.

[`examples`]: https://github.com/madsmtm/objc2/tree/master/objc2-encode/examples
This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
see that for related crates.
25 changes: 21 additions & 4 deletions objc2-encode/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -6,15 +6,32 @@ use crate::parse;
///
/// Can be retrieved in Objective-C for a type `T` using the `@encode(T)`
/// directive.
/// ```objective-c , ignore
/// ```objc
/// NSLog(@"Encoding of NSException: %s", @encode(NSException));
/// ```
///
/// For more information, see [Apple's documentation][ocrtTypeEncodings] and
/// [`clang`'s source code for generating `@encode`][clang-src].
/// The [`Display`][`fmt::Display`] implementation converts the [`Encoding`]
/// into its string representation, that the the `@encode` directive would
/// return. This can be used conveniently through the `to_string` method:
///
/// ```
/// use objc2_encode::Encoding;
/// assert_eq!(Encoding::Int.to_string(), "i");
/// ```
///
/// For more information on the string value of an encoding, see [Apple's
/// documentation][ocrtTypeEncodings].
///
/// [ocrtTypeEncodings]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
/// [clang-src]: https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850
///
/// # Examples
///
/// Comparing an encoding to a string from the Objective-C runtime:
///
/// ```
/// use objc2_encode::Encoding;
/// assert!(Encoding::Array(10, &Encoding::FloatComplex).equivalent_to_str("[10jf]"));
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive] // Maybe we're missing some encodings?
pub enum Encoding<'a> {
64 changes: 61 additions & 3 deletions objc2-encode/src/lib.rs
Original file line number Diff line number Diff line change
@@ -2,10 +2,68 @@
//!
//! This is re-exported into the top level of `objc2`.
//!
//! The Objective-C directive `@encode` encodes types as strings, and this is
//! used in various places in the runtime.
//!
//! This crate provides the [`Encoding`] type to efficiently describe and
//! compare these type-encodings.
//!
//! Additionally it provides traits for annotating types that has an
//! Objective-C encoding: Specifically [`Encode`] for structs, [`RefEncode`]
//! for references and [`EncodeArguments`] for function arguments.
//!
//! These types are exported under the [`objc2`] crate as well, so usually you
//! would just use them from there.
//!
//! ## Example
//!
//! Implementing [`Encode`] and [`RefEncode`] for a custom type:
//!
//! ```
//! use objc2_encode::{Encode, Encoding, RefEncode};
//! // or from objc2:
//! // use objc2::{Encode, Encoding, RefEncode};
//!
//! #[repr(C)]
//! struct MyStruct {
//! a: f32, // float
//! b: i16, // int16_t
//! }
//!
//! unsafe impl Encode for MyStruct {
//! const ENCODING: Encoding<'static> = Encoding::Struct(
//! "MyStruct", // Must use the same name as defined in C header files
//! &[
//! f32::ENCODING, // Same as Encoding::Float
//! i16::ENCODING, // Same as Encoding::Short
//! ],
//! );
//! }
//!
//! // @encode(MyStruct) -> "{MyStruct=fs}"
//! assert!(MyStruct::ENCODING.equivalent_to_str("{MyStruct=fs}"));
//!
//! unsafe impl RefEncode for MyStruct {
//! // Note that if `MyStruct` is an Objective-C instance, this should
//! // be `Encoding::Object`.
//! const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
//! }
//!
//! // @encode(MyStruct*) -> "^{MyStruct=fs}"
//! assert!(MyStruct::ENCODING_REF.equivalent_to_str("^{MyStruct=fs}"));
//! ```
//!
//! See the [`examples`] folder for more complex usage.
//!
//! [`examples`]: https://github.com/madsmtm/objc2/tree/master/objc2-encode/examples
//!
//! Further resources:
//! - <https://dmaclach.medium.com/objective-c-encoding-and-you-866624cc02de>
//! - <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html>
//! - <https://dmaclach.medium.com/objective-c-encoding-and-you-866624cc02de>
//! - [Objective-C, Encoding and You](https://dmaclach.medium.com/objective-c-encoding-and-you-866624cc02de).
//! - [Apple's documentation on Type Encodings](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html).
//! - [How are the digits in ObjC method type encoding calculated?](https://stackoverflow.com/a/11527925)
//! - [`clang`'s source code for generating `@encode`](https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850).
//!
//! [`objc2`]: https://crates.io/crates/objc2
#![no_std]
#![warn(elided_lifetimes_in_paths)]
2 changes: 1 addition & 1 deletion objc2-foundation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ version = "0.2.0-alpha.3" # Remember to update html_root_url in lib.rs
authors = ["Steven Sheldon", "Mads Marquart <mads@marquart.dk>"]
edition = "2018"

description = "Bindings to the Objective-C Foundation framework"
description = "Bindings to the Objective-C Cocoa Foundation framework"
keywords = ["objective-c", "macos", "ios", "cocoa", "uikit"]
categories = [
"api-bindings",
10 changes: 9 additions & 1 deletion objc2-foundation/README.md
Original file line number Diff line number Diff line change
@@ -5,4 +5,12 @@
[![Documentation](https://docs.rs/objc2-foundation/badge.svg)](https://docs.rs/objc2-foundation/)
[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)

Bindings to the Objective-C `Foundation` framework in Rust.
Bindings to the Objective-C Cocoa `Foundation` framework in Rust.

This library is very much in progress, consider using the more battle-tested
[`cocoa-foundation`] crate in the meantime.

This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
see that for related crates.

[`cocoa-foundation`]: https://crates.io/crates/cocoa-foundation
7 changes: 7 additions & 0 deletions objc2-foundation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! # Bindings to the Objective-C Cocoa `Foundation` framework
//!
//! This library is very much in progress, consider using the more
//! battle-tested [`cocoa-foundation`] crate in the meantime.
//!
//! [`cocoa-foundation`]: https://crates.io/crates/cocoa-foundation
#![no_std]
#![warn(elided_lifetimes_in_paths)]
#![deny(non_ascii_idents)]
1 change: 1 addition & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* **BREAKING**: Renamed:
- `Object::get_ivar` -> `Object::ivar`
- `Object::get_mut_ivar` -> `Object::ivar_mut`
* Vastly improved documentation.


## 0.3.0-alpha.5 - 2021-12-22
4 changes: 3 additions & 1 deletion objc2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ version = "0.3.0-alpha.5" # Remember to update html_root_url in lib.rs
authors = ["Steven Sheldon", "Mads Marquart <mads@marquart.dk>"]
edition = "2018"

description = "Objective-C runtime bindings and interface."
description = "Objective-C interface and runtime bindings"
keywords = ["objective-c", "macos", "ios", "objc_msgSend"]
categories = [
"api-bindings",
@@ -18,6 +18,8 @@ license = "MIT"

build = "build.rs"

# NOTE: 'unstable' features are _not_ considered part of the SemVer contract,
# and may be removed in a minor release.
[features]
# Enables `objc2::exception::throw` and `objc2::exception::catch`
exception = ["cc"]
117 changes: 18 additions & 99 deletions objc2/README.md
Original file line number Diff line number Diff line change
@@ -5,113 +5,32 @@
[![Documentation](https://docs.rs/objc2/badge.svg)](https://docs.rs/objc2/)
[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)

Objective-C runtime bindings and interface for Rust.
Objective-C interface and runtime bindings for Rust.

## Messaging objects
Most of the core libraries and frameworks that are in use on Apple systems are
written in Objective-C; this crate enables you to interract with those.

Objective-C objects can be messaged using the `msg_send!` macro:
## Example

```rust , no_run
```rust
use std::ptr::NonNull;
use objc2::{class, msg_send};
use objc2::runtime::{Bool, Object};
use objc2::rc::{Id, Owned};
use objc2::runtime::{Class, Object};

let cls = class!(NSObject);
unsafe {
let obj: *mut Object = msg_send![cls, new];
let hash: usize = msg_send![obj, hash];
let is_kind: Bool = msg_send![obj, isKindOfClass: cls];
// Even void methods must have their return type annotated
let _: () = msg_send![obj, release];
}
```

## Reference counting

The utilities of the `rc` module provide ARC-like semantics for working with
Objective-C's reference counted objects in Rust.

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, Id, Shared, WeakId};
use objc2::runtime::Object;

// 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(|pool| {
// Autorelease consumes the Id, but won't
// actually release until the end of an autoreleasepool
let obj_ref: &Object = cloned.autorelease(pool);
});

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

## Declaring classes
let obj: *mut Object = unsafe { msg_send![cls, new] };
let obj: Id<Object, Owned> = unsafe { Id::new(NonNull::new(obj).unwrap()) };

Classes can be declared using the `ClassDecl` struct. Instance variables and
methods can then be added before the class is ultimately registered.

The following example demonstrates declaring a class named `MyNumber` that has
one ivar, a `u32` named `_number` and a `number` method that returns it:

```rust , no_run
use objc2::{class, sel};
use objc2::declare::ClassDecl;
use objc2::runtime::{Object, Sel};

let superclass = class!(NSObject);
let mut decl = ClassDecl::new("MyNumber", superclass).unwrap();

// Add an instance variable
decl.add_ivar::<u32>("_number");

// Add an ObjC method for getting the number
extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
unsafe { *this.ivar("_number") }
}
unsafe {
decl.add_method(sel!(number),
my_number_get as extern fn(&Object, Sel) -> u32);
}

decl.register();
// TODO
// let isa = unsafe { obj.ivar::<Class>("isa") };
// assert_eq!(cls, isa);
```

## Exceptions

By default, if the `msg_send!` macro causes an exception to be thrown, this
will unwind into Rust resulting in unsafe, undefined behavior.
However, this crate has an `"catch_all"` feature which, when enabled, wraps
each `msg_send!` in a `@try`/`@catch` and panics if an exception is caught,
preventing Objective-C from unwinding into Rust.

## Message type verification

The Objective-C runtime includes encodings for each method that describe the
argument and return types. This crate can take advantage of these encodings to
verify that the types used in Rust match the types encoded for the method.

To use this functionality, enable the `"verify_message"` feature.
With this feature enabled, type checking is performed for every message send,
which also requires that all arguments and return values for all messages
implement `Encode`.

If this requirement is burdensome or you'd rather just verify specific messages,
you can call the `Message::verify_message` method for specific selectors.
See [the docs](https://docs.rs/objc2/) for a more thorough overview, or jump
right into the [examples].

## Support for other Operating Systems
This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
see that for related crates.

The bindings can be used on Linux or *BSD utilizing the
[GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2).
[examples]: https://github.com/madsmtm/objc2/tree/master/objc2/examples
1 change: 1 addition & 0 deletions objc2/examples/introspection.rs
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ fn main() {
println!("NSObject address: {:p}", obj);

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

17 changes: 10 additions & 7 deletions objc2/src/declare.rs
Original file line number Diff line number Diff line change
@@ -9,23 +9,26 @@
//! has one ivar, a `u32` named `_number` and a `number` method that returns
//! it:
//!
//! ``` no_run
//! # use objc2::{class, sel};
//! # use objc2::declare::ClassDecl;
//! # use objc2::runtime::{Class, Object, Sel};
//! ```no_run
//! use objc2::{class, sel};
//! use objc2::declare::ClassDecl;
//! use objc2::runtime::{Class, Object, Sel};
//!
//! let superclass = class!(NSObject);
//! let mut decl = ClassDecl::new("MyNumber", superclass).unwrap();
//!
//! // Add an instance variable
//! decl.add_ivar::<u32>("_number");
//!
//! // Add an ObjC method for getting the number
//! extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
//! extern "C" fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
//! unsafe { *this.ivar("_number") }
//! }
//! unsafe {
//! decl.add_method(sel!(number),
//! my_number_get as extern fn(&Object, Sel) -> u32);
//! decl.add_method(
//! sel!(number),
//! my_number_get as extern "C" fn(&Object, Sel) -> u32,
//! );
//! }
//!
//! decl.register();
8 changes: 7 additions & 1 deletion objc2/src/exception.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
//! Objective-C's @throw and @try/@catch.
//!
//! This is only available when the `exception` feature is enabled.
//! By default, if the [`msg_send!`] macro causes an exception to be thrown,
//! this will unwind into Rust, resulting in undefined behavior. However, this
//! crate has an `"catch_all"` feature which, when enabled, wraps each
//! [`msg_send!`] in a [`catch`] and panics if an exception is caught,
//! preventing Objective-C from unwinding into Rust.
//!
//! This module is only available when the `"exception"` feature is enabled.
//!
//! See the following links for more information:
//! - <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Tasks/HandlingExceptions.html>
147 changes: 105 additions & 42 deletions objc2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,124 @@
//! Objective-C Runtime bindings and wrapper for Rust.
//! # Objective-C interface and runtime bindings
//!
//! # Messaging objects
//! Objective-C is<sup>1</sup> the standard programming language on Apple
//! platforms like macOS, iOS, tvOS and watchOS. It is an object-oriented
//! language centered around sending messages to it's instances, which is for
//! the most part equivalent to a function call.
//!
//! Objective-C objects can be messaged using the
//! [`msg_send!`](macro.msg_send!.html) macro:
//! Most of the core libraries and frameworks that are in use on Apple systems
//! are written in Objective-C, and hence we would like the ability to
//! interract with these using Rust; this crate enables you to do that, in
//! as safe a manner as possible.
//!
//! ``` no_run
//! # use objc2::{class, msg_send};
//! # use objc2::runtime::{Bool, Class, Object};
//! # unsafe {
//! <sup>1: Yes, I know, "was", Swift now exists. All the existing frameworks
//! are written in Objective-C though, so the point still holds.</sup>
//!
//!
//! ## Basic usage
//!
//! This example illustrates major parts of the functionality in this crate:
//!
//! First, we get a reference to the `NSObject`'s [`runtime::Class`] using the
//! [`class!`] macro.
//! Next, we creates a new [`runtime::Object`] pointer, and ensures it is
//! deallocated after we've used it by putting it into an [`rc::Owned`]
//! [`rc::Id`].
//! Now we send messages to the object to our hearts desire using
//! the [`msg_send!`] macro, and lastly, the `Id<Object, _>` goes out of
//! scope, and the object is deallocated.
//!
#![cfg_attr(apple, doc = "```")]
#![cfg_attr(not(apple), doc = "```no_run")]
//! use core::ptr::NonNull;
//! use objc2::{class, msg_send};
//! use objc2::ffi::NSUInteger;
//! use objc2::rc::{Id, Owned};
//! use objc2::runtime::{Bool, Object};
//!
//! // Creation
//! let cls = class!(NSObject);
//! let obj: *mut Object = msg_send![cls, new];
//! let hash: usize = msg_send![obj, hash];
//! let is_kind: Bool = msg_send![obj, isKindOfClass: cls];
//! // Even void methods must have their return type annotated
//! let _: () = msg_send![obj, release];
//! # }
//! ```
//! let obj: *mut Object = unsafe { msg_send![cls, new] };
//! let obj = NonNull::new(obj).expect("Failed allocating object");
//! let obj: Id<Object, Owned> = unsafe { Id::new(obj) };
//!
//! # Reference counting
//! // Usage
//! let hash: NSUInteger = unsafe { msg_send![obj, hash] };
//! let is_kind: Bool = unsafe { msg_send![obj, isKindOfClass: cls] };
//! assert!(is_kind.is_true());
//! ```
//!
//! Utilities for reference counting Objective-C objects are provided in the
//! [`rc`](rc/index.html) module.
//! Note that this very simple example contains **a lot** of `unsafe` (which
//! should all ideally be justified with a `// SAFETY` comment). This is
//! required because our compiler can verify very little about the Objective-C
//! invocation, including all argument and return types used in [`msg_send!`];
//! we could have just as easily accidentally made `hash` an `f32`, or any
//! other type, and this would trigger undefined behaviour!
//!
//! # Declaring classes
//! Making the ergonomics better is something that is currently being worked
//! on, see e.g. the [`objc2-foundation`] crate for more ergonomic usage of at
//! least the `Foundation` framework.
//!
//! Objective-C classes can even be declared from Rust using the functionality
//! of the [`declare`](declare/index.html) module.
//! Anyhow, this nicely leads us to another feature that this crate has:
//!
//! # Exceptions
//! [`runtime::Class`]: crate::runtime::Class
//! [`runtime::Object`]: crate::runtime::Object
//! [`rc::Owned`]: crate::rc::Owned
//! [`rc::Id`]: crate::rc::Id
//! [`objc2-foundation`]: https://crates.io/crates/objc2-foundation
//!
//! By default, if the `msg_send!` macro causes an exception to be thrown,
//! this will unwind into Rust resulting in unsafe, undefined behavior.
//! However, this crate has an `"catch_all"` feature which, when enabled,
//! wraps each `msg_send!` in a `@try`/`@catch` and panics if an exception is
//! caught, preventing Objective-C from unwinding into Rust.
//!
//! # Message type verification
//! ## Encodings and message type verification
//!
//! The Objective-C runtime includes encodings for each method that describe
//! the argument and return types. This crate can take advantage of these
//! encodings to verify that the types used in Rust match the types encoded
//! for the method.
//! the argument and return types. See the [`objc2-encode`] crate for the
//! full overview of what this is.
//!
//! The important part is, to make message sending _safer_ (not fully safe),
//! all arguments and return values for messages must implement [`Encode`].
//! This allows the Rust compiler to prevent you from passing e.g. a [`Box`]
//! into Objective-C, which would both be UB and leak the box.
//!
//! Furthermore, this crate can take advantage of the encodings provided by
//! the runtime to verify that the types used in Rust match the types encoded
//! for the method. This is not a perfect solution for ensuring safety of
//! message sends (some Rust types have the same encoding, but are not
//! equivalent), but it gets us much closer to it!
//!
//! To use this functionality, enable the `"verify_message"` feature.
//! With this feature enabled, type checking is performed for every message
//! send, which also requires that all arguments and return values for all
//! messages implement `Encode`.
//! To use this functionality, enable the `"verify_message"` cargo feature
//! while debugging. With this feature enabled, encoding types are checked
//! every time your send a message, and the message send will panic if they
//! are not equivalent.
//!
//! If this requirement is burdensome or you'd rather
//! just verify specific messages, you can call the
//! [`Message::verify_message`](trait.Message.html#method.verify_message)
//! method for specific selectors.
//! [`objc2-encode`]: https://crates.io/crates/objc2-encode
//! [`Box`]: std::boxed::Box
//!
//! # Support for other Operating Systems
//!
//! ## Crate features
//!
//! This crate exports several optional cargo features, see [`Cargo.toml`] for
//! an overview and description of these.
//!
//! [`Cargo.toml`]: https://github.com/madsmtm/objc2/blob/master/objc2/Cargo.toml
//!
//!
//! ## Support for other Operating Systems
//!
//! The bindings can be used on Linux or *BSD utilizing the
//! [GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2).
//! [GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2),
//! see the [`objc-sys`][`objc_sys`] crate for how to configure this.
//!
//!
//! ## Other features
//!
//! Anyhow, that was a quick introduction, this library also has [support for
//! handling exceptions][exc], [the ability to dynamically declare Objective-C
//! classes][declare], [more advanced reference-counting utilities][rc] and
//! more, peruse the documentation at will!
//!
#![cfg_attr(feature = "exception", doc = "[exc]: crate::exception")]
#![cfg_attr(not(feature = "exception"), doc = "[exc]: #exception-feature-disabled")]
//! [declare]: crate::declare
//! [rc]: crate::rc
#![no_std]
#![cfg_attr(
@@ -74,7 +136,8 @@
extern crate alloc;
extern crate std;

#[cfg(doctest)]
// The example uses NSObject without doing the __gnustep_hack
#[cfg(all(apple, doctest))]
#[doc = include_str!("../README.md")]
extern "C" {}

4 changes: 3 additions & 1 deletion objc2/src/macros.rs
Original file line number Diff line number Diff line change
@@ -61,7 +61,8 @@ macro_rules! sel {
/// Sends a message to an object or class.
///
/// The first argument can be any type that implements [`MessageReceiver`],
/// like a reference, a pointer, or an [`rc::Id`] to an object.
/// like a reference, a pointer, or an [`rc::Id`] to an object (where the
/// object implements [`Message`]).
///
/// In general this is wildly `unsafe`, even more so than sending messages in
/// Objective-C, because this macro doesn't know the expected types and
@@ -73,6 +74,7 @@ macro_rules! sel {
/// Variadic arguments are not currently supported.
///
/// [`MessageReceiver`]: crate::MessageReceiver
/// [`Message`]: crate::Message
/// [`rc::Id`]: crate::rc::Id
///
/// # Panics
3 changes: 2 additions & 1 deletion objc2/src/message/mod.rs
Original file line number Diff line number Diff line change
@@ -188,7 +188,8 @@ pub unsafe trait MessageReceiver: private::Sealed {
/// arguments `A` and return type `R`.
///
/// # Example
/// ``` no_run
///
/// ```no_run
/// # use objc2::{class, msg_send, sel};
/// # use objc2::runtime::{Bool, Class, Object};
/// # use objc2::MessageReceiver;
11 changes: 4 additions & 7 deletions objc2/src/rc/autorelease.rs
Original file line number Diff line number Diff line change
@@ -233,7 +233,7 @@ impl !AutoreleaseSafe for AutoreleasePool {}
///
/// Basic usage:
///
/// ```rust,no_run
/// ```no_run
/// use objc2::{class, msg_send};
/// use objc2::rc::{autoreleasepool, AutoreleasePool};
/// use objc2::runtime::Object;
@@ -256,7 +256,7 @@ impl !AutoreleaseSafe for AutoreleasePool {}
/// Fails to compile because `obj` does not live long enough for us to
/// safely take it out of the pool:
///
/// ```rust,compile_fail
/// ```compile_fail
/// # use objc2::{class, msg_send};
/// # use objc2::rc::{autoreleasepool, AutoreleasePool};
/// # use objc2::runtime::Object;
@@ -277,11 +277,8 @@ impl !AutoreleaseSafe for AutoreleasePool {}
/// Incorrect usage which panics because we tried to pass an outer pool to an
/// inner pool:
///
#[cfg_attr(feature = "unstable_autoreleasesafe", doc = "```rust,compile_fail")]
#[cfg_attr(
not(feature = "unstable_autoreleasesafe"),
doc = "```rust,should_panic"
)]
#[cfg_attr(feature = "unstable_autoreleasesafe", doc = "```compile_fail")]
#[cfg_attr(not(feature = "unstable_autoreleasesafe"), doc = "```should_panic")]
/// # use objc2::{class, msg_send};
/// # use objc2::rc::{autoreleasepool, AutoreleasePool};
/// # use objc2::runtime::Object;
43 changes: 36 additions & 7 deletions objc2/src/rc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,59 @@
//! Utilities for reference counting Objective-C objects.
//!
//! The utilities of the `rc` module provide ARC-like semantics for working
//! with Objective-C's reference counted objects in Rust.
//! These utilities in this module provide ARC-like semantics for working with
//! Objective-C's reference counted objects.
//!
//! A smart pointer [`Id`] is provided to ensure that Objective-C objects are
//! retained and released at the proper times.
//! retained and released when created and dropped, respectively.
//!
//! 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
//! mutably dereferenced. An owned `Id` can be downgraded to a shared `Id`
//! which can be cloned to allow multiple references.
//!
//! Weak references may be created using the [`WeakId`] struct.
//! Weak references may be created using the [`WeakId`] struct; these will not
//! retain the object, but they can upgraded to an `Id` in a manner that
//! safely fails if the object has been deallocated.
//!
//! See [the clang documentation][clang-arc] and [the Apple article on memory
//! management][mem-mgmt] (similar document exists [for Core Foundation][mem-cf])
//! management][mem-mgmt] (similar document exists [for Core Foundation][cf])
//! for more information on automatic and manual reference counting.
//!
//! It can also be useful to [enable Malloc Debugging][mem-debug] if you're trying
//! to figure out if/where your application has memory errors and leaks.
//!
//!
//! [clang-arc]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html
//! [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
//! [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
//!
#![cfg_attr(apple, doc = "```")]
#![cfg_attr(not(apple), doc = "```no_run")]
//! use objc2::{class, msg_send};
//! use objc2::rc::{autoreleasepool, Id, Shared, WeakId};
//! use objc2::runtime::Object;
//!
//! // 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(|pool| {
//! // Autorelease consumes the Id, but won't
//! // actually release until the end of an autoreleasepool
//! let obj_ref: &Object = cloned.autorelease(pool);
//! });
//!
//! // Weak references won't retain the object
//! let weak = WeakId::new(&obj);
//! drop(obj);
//! assert!(weak.load().is_none());
//! ```
mod autorelease;
mod id;

0 comments on commit 0da814d

Please sign in to comment.