class UI::NativeHandle

Overview

Wraps a raw Void* pointer to a platform-native object with explicit ownership semantics determined by a ReleaseStrategy.

Deterministic Cleanup vs GC Safety Net

The preferred cleanup path is #release!, which immediately frees the native resource and poisons the pointer to catch use-after-free bugs. The #finalize method acts as a GC safety net for handles that were not explicitly released -- it should NOT be relied upon as the primary cleanup mechanism.

Thread Safety

NativeHandle instances are NOT thread-safe. A handle should be owned by a single NativeView and released on the same thread that created it.

Usage

handle = UI::NativeHandle.new(some_ptr, UI::ReleaseStrategy::ObjCRelease, label: "NSButton")
handle.valid?  # => true
handle.ptr!    # => some_ptr (raises if released or null)
handle.release!
handle.valid?  # => false
handle.release! # safe: idempotent, no-op on second call

Defined in:

ui/native/native_handle.cr

Constructors

Instance Method Summary

Constructor Detail

def self.new(ptr : Pointer(Void), strategy : ReleaseStrategy, label : String | Nil = nil) #

[View source]

Instance Method Detail

def finalize #

GC safety net. If the handle was never explicitly released, attempt to clean up the native resource. This is a last resort -- always prefer calling #release! or NativeView#teardown! explicitly.


[View source]
def label : String | Nil #

Optional debug label identifying what this handle wraps. Only used for diagnostics and leak tracking.


[View source]
def ptr : Pointer(Void) #

The raw pointer to the platform-native object. After #release!, this is poisoned to Pointer(Void).null.


[View source]
def ptr! : Pointer(Void) #

Returns the raw pointer, raising if the handle has been released or the pointer is null.

Use this when you need a guaranteed-valid pointer for FFI calls.

Raises Exception if the handle is released or wraps a null pointer.


[View source]
def release! : Nil #

Deterministic cleanup. Releases the native resource according to the strategy and poisons the pointer.

This method is idempotent: calling it multiple times is safe and has no effect after the first call.


[View source]
def released? : Bool #

Whether #release! has been called. Once true, the handle is dead.


[View source]
def state_handle : Pointer(Void) | Nil #

Optional reactive-state pointer (Phase 3 Remediation 4).

When the wrapped platform view was constructed through one of the apsk_make_*_reactive facade entry points, the Swift side allocates an ObservableObject state container (APSKLabelState, APSKButtonState, BoolStorage, DoubleStorage) and writes a +1 retained opaque pointer here. The matching Crystal mutator method (UI::Label#text=, etc.) dispatches through this pointer to update the published value, which drives a SwiftUI re-render of the hosted subtree without rebuilding the view tree.

#release! drops the +1 retain via apsk_state_release so the state object's lifetime tracks the platform view's. Nil when the handle was built through the legacy non-reactive path or wraps a view that does not participate in the reactive surface (every widget today except Label / Button / Toggle / Slider).


[View source]
def state_handle=(state_handle : Pointer(Void) | Nil) #

Optional reactive-state pointer (Phase 3 Remediation 4).

When the wrapped platform view was constructed through one of the apsk_make_*_reactive facade entry points, the Swift side allocates an ObservableObject state container (APSKLabelState, APSKButtonState, BoolStorage, DoubleStorage) and writes a +1 retained opaque pointer here. The matching Crystal mutator method (UI::Label#text=, etc.) dispatches through this pointer to update the published value, which drives a SwiftUI re-render of the hosted subtree without rebuilding the view tree.

#release! drops the +1 retain via apsk_state_release so the state object's lifetime tracks the platform view's. Nil when the handle was built through the legacy non-reactive path or wraps a view that does not participate in the reactive surface (every widget today except Label / Button / Toggle / Slider).


[View source]
def strategy : ReleaseStrategy #

The ownership strategy that determines how the pointer is released.


[View source]
def valid? : Bool #

Returns true if the handle wraps a non-null pointer and has not been released.


[View source]