objc

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Rendered for darwin/amd64

Overview

Package objc provides the CGo runtime layer shared by all generated framework packages.

It defines the Object interface that every generated ObjC wrapper type satisfies (exposing its underlying ObjC pointer via Ptr), and supplies the low-level operations that generated code relies on at runtime:

  • Memory management: Retain and Release wrap ObjC retain/release; all bridge functions return +1-retained pointers and a Go finalizer calls Release so the caller never needs to manage object lifetimes manually.
  • Main-thread dispatch: RunOnMainThread executes a closure on the main GCD queue using dispatch_sync_f, which is required for all AppKit and UIKit calls.
  • String conversion: helpers to marshal between NSString * and Go string.
  • ObjC exception bridging: utilities used by [tel] to convert a caught NSException back into a Go panic.

All files in this package are compiled with -fno-objc-arc; reference counting is handled explicitly through the functions above.

Index

Constants

View Source
const (
	AssocAssign          uintptr = 0
	AssocRetainNonatomic uintptr = 1
	AssocCopyNonatomic   uintptr = 3
	AssocRetain          uintptr = 0x301
	AssocCopy            uintptr = 0x303
)

Association policies for SetAssociatedObject, mirroring objc_AssociationPolicy.

Variables

View Source
var OnMainThreadPanic func(r interface{}, stack []byte)

OnMainThreadPanic is called when a panic is recovered inside a RunOnMainThread or DispatchAsyncMain closure. Wire this to your structured logger at application startup. Called on the main OS thread; must not block.

Functions

func AllocObject

func AllocObject(className string) unsafe.Pointer

AllocObject calls +alloc on the named ObjC class and returns the uninitialized +1-retained pointer. The caller must immediately pass it to an init method. Returns nil if the class is not registered at runtime.

func ClassNameOf

func ClassNameOf(ptr unsafe.Pointer) string

ClassNameOf returns the ObjC runtime class name of the object at ptr, or "" if ptr is nil. Wraps object_getClass() + class_getName().

func DispatchAsyncMain

func DispatchAsyncMain(f func())

DispatchAsyncMain schedules f to run on the macOS main OS thread without blocking. The function will be called during the next available main run loop iteration. Use this to schedule UI setup after [NSApp run] has started.

func ExceptionReason

func ExceptionReason(ptr unsafe.Pointer) string

ExceptionReason extracts the reason string from a caught ObjC NSException. Deprecated: prefer ExceptionInfoFromPtr for structured access.

func FreePtr

func FreePtr(ptr unsafe.Pointer)

FreePtr releases a heap-allocated C pointer returned by the bridge layer. Used by generated code to free the malloc'd struct buffer after copying it into a Go value (value-type struct returns).

func GetAssociatedObject

func GetAssociatedObject(obj unsafe.Pointer, key string) unsafe.Pointer

GetAssociatedObject retrieves the value previously attached to obj under key. Returns nil if no value has been set.

func GoStringToNSString

func GoStringToNSString(s string) unsafe.Pointer

GoStringToNSString converts a Go string to an ObjC NSString (+1 retain). The caller (or finalizer) is responsible for releasing the returned pointer.

func InstallCrashHandlers

func InstallCrashHandlers()

InstallCrashHandlers installs an ObjC uncaught-exception handler that writes the exception name, reason, and full call-stack symbols to stderr before the process terminates. Call this as early as possible in main().

func KeepAlive

func KeepAlive(v any)

KeepAlive marks v as live until the call to KeepAlive, preventing the GC from finalizing ObjC wrapper objects before a CGo call using the raw pointer has completed.

func MakeIMP

func MakeIMP(block unsafe.Pointer) unsafe.Pointer

MakeIMP converts an ObjC block (created via imp_implementationWithBlock) into a C IMP function pointer suitable for MethodBinding.Imp.

block must be an ObjC block value (id). Ownership of the block is transferred to the IMP; do not release it after calling MakeIMP.

func NSErrorToError

func NSErrorToError(ptr unsafe.Pointer) error

NSErrorToError converts an ObjC NSError pointer to a Go error. Releases the pointer and returns nil if ptr is nil.

func NSStringToGoString

func NSStringToGoString(ptr unsafe.Pointer) string

NSStringToGoString converts an ObjC NSString pointer to a Go string. Returns an empty string if ptr is nil.

func PumpMainRunLoop

func PumpMainRunLoop(seconds float64)

PumpMainRunLoop runs the main NSRunLoop for up to seconds seconds, then returns. Drains pending main-queue dispatch items; useful for test binaries that need RunOnMainThread to work without a full NSApplicationMain run loop.

func Release

func Release(ptr unsafe.Pointer)

Release calls ObjC release on ptr immediately.

func RemoveAssociatedObjects

func RemoveAssociatedObjects(obj unsafe.Pointer)

RemoveAssociatedObjects removes all associated objects from obj.

func Retain

func Retain(ptr unsafe.Pointer) unsafe.Pointer

Retain calls ObjC retain on ptr and returns ptr.

func RunOnMainThread

func RunOnMainThread(f func())

RunOnMainThread executes f on the macOS main OS thread and blocks until f returns. Required for all AppKit/UIKit operations.

If the calling goroutine is already running on the main thread, f is called inline without dispatching.

func Sel

func Sel(name string) unsafe.Pointer

Sel returns the ObjC SEL for name, caching after the first lookup. Equivalent to @selector(name) in Objective-C.

func SetAssociatedObject

func SetAssociatedObject(obj unsafe.Pointer, key string, value unsafe.Pointer, policy uintptr)

SetAssociatedObject attaches value to obj under key using the given policy. key is a Go string; pointer identity is handled internally via a stable cache.

func SetDeallocListener

func SetDeallocListener(obj unsafe.Pointer, listener func())

SetDeallocListener fires listener exactly once when obj is deallocated by the ObjC runtime. This is the correct mechanism to notify Go when an ObjC object it holds a reference to is being torn down — for example, to clean up Go closures stored via associated objects that would otherwise be orphaned.

Internally this attaches a GoObjcDeallocListener as a retained associated object on obj. When obj is released, ObjC clears its associated objects, releasing the listener, whose -dealloc fires the callback.

func Track

func Track(wrapper any, ptrFn func() unsafe.Pointer)

Track registers a finalizer on wrapper that calls Release on its ObjC pointer when the GC collects the wrapper. ptrFn must return the wrapper's ptr field.

AppKit objects (NSView and its subclasses) perform CoreAnimation layer cleanup in -dealloc that must run on the main thread. Go's finalizer goroutine is never the main thread, so we always dispatch the release to the main queue rather than calling CFRelease inline. This prevents kCAValueWeakPointer / NSViewBackingLayer corruption that causes CA::AttrList::get crashes on the next AppKit call.

func WithAutoreleasePool

func WithAutoreleasePool(task func())

WithAutoreleasePool runs task inside an NSAutoreleasePool drain cycle.

Any ObjC work performed outside the main application run loop — including goroutines, test helpers, and background tasks — must be wrapped in this function to prevent autorelease objects from leaking.

func WrapTyped

func WrapTyped[T any](ptr unsafe.Pointer, constructor func(unsafe.Pointer) T) (result T)

WrapTyped wraps an ObjC pointer in a typed Go wrapper using the provided constructor. The constructor is called only when ptr is non-nil. It is the centralised nil-guard used by generated method returns and block callbacks:

// generated method return
return objc.WrapTyped(_ptr, NewVZVirtualMachine)

// generated block callback
blocks.MakeBlock_void_ptr(func(_p0 unsafe.Pointer) {
    completionHandler(objc.WrapTyped(_p0, NewVZMacOSRestoreImage), objc.NSErrorToError(_p1))
})

constructor must not be nil.

Types

type ExceptionInfo

type ExceptionInfo struct {
	// Name is the exception class name (e.g. "NSInvalidArgumentException").
	Name string
	// Reason is the human-readable failure description.
	Reason string
}

ExceptionInfo holds structured fields from a caught ObjC NSException.

func ExceptionInfoFromPtr

func ExceptionInfoFromPtr(ptr unsafe.Pointer) ExceptionInfo

ExceptionInfoFromPtr extracts structured fields from a caught ObjC NSException, releases the exception object, and returns the populated ExceptionInfo. Returns zero value if ptr is nil.

type MethodBinding

type MethodBinding struct {
	// Selector is the ObjC selector string, e.g. "applicationDidFinishLaunching:".
	Selector string
	// TypeEncoding is the ObjC type encoding for the method, e.g. "v@:@".
	TypeEncoding string
	// Imp is the C function pointer for this method's implementation.
	// Obtain via MakeIMP or runtime_imp_from_block.
	Imp unsafe.Pointer
}

MethodBinding describes one ObjC method implementation to install on a user class. Imp must be a C function pointer whose signature matches TypeEncoding, typically produced by imp_implementationWithBlock (runtime_imp_from_block).

type NSError

type NSError struct {
	// contains filtered or unexported fields
}

NSError wraps an ObjC NSError pointer as a Go error.

func (*NSError) Error

func (e *NSError) Error() string

func (*NSError) Ptr

func (e *NSError) Ptr() unsafe.Pointer

type Object

type Object interface {
	Ptr() unsafe.Pointer
}

Object is the constraint satisfied by every generated ObjC wrapper type. It guarantees the type exposes its underlying ObjC pointer.

func WrapObject

func WrapObject(ptr unsafe.Pointer) Object

WrapObject wraps an ObjC pointer in a minimal Object implementation. Used when a method's return type is a generic type parameter and the concrete wrapper type is not known at generation time (e.g. NSArray[T].firstObject). Returns nil when ptr is nil.

type PoolScope

type PoolScope struct {
	// contains filtered or unexported fields
}

PoolScope represents an explicit autorelease pool boundary. Use NewPoolScope for code paths that create many short-lived ObjC objects outside a normal run-loop cycle (e.g., tight loops over NSString conversions). Call Drain when the scope exits.

func NewPoolScope

func NewPoolScope() PoolScope

NewPoolScope pushes a new autorelease pool and returns the scope handle. The caller must call Drain() to pop the pool and release its contents. Typically used with defer:

scope := objc.NewPoolScope()
defer scope.Drain()

func (*PoolScope) Drain

func (s *PoolScope) Drain()

Drain pops the autorelease pool, releasing all autoreleased objects in the scope. Safe to call multiple times (subsequent calls are no-ops).

type SyncCache

type SyncCache[K comparable, V any] struct {
	// contains filtered or unexported fields
}

SyncCache is a thread-safe compute-once cache. The first call to Load for a given key invokes compute; subsequent calls return the cached value.

func (*SyncCache[K, V]) Load

func (c *SyncCache[K, V]) Load(key K, compute func(K) V) V

Load returns the cached value for key, computing it via compute on first access.

type UserClassHandle

type UserClassHandle struct {
	// contains filtered or unexported fields
}

UserClassHandle represents a Go-backed ObjC class registered with the runtime. Instances are created via NewInstance; the class lives for the process lifetime.

func RegisterUserClass

func RegisterUserClass(name, superName string, protocols []string, methods []MethodBinding) *UserClassHandle

RegisterUserClass allocates and registers an ObjC class named name as a subclass of superName, conforms it to the named protocols, and installs each method binding. Panics if the class is already registered or if superName does not exist in the ObjC runtime.

Classes are process-lifetime objects. Call this once at startup (or via sync.Once) rather than per-request.

func (*UserClassHandle) Dispose

func (h *UserClassHandle) Dispose()

Dispose disposes the class pair. Must only be called after all instances have been released. Classes are normally process-lifetime — only dispose in tests.

func (*UserClassHandle) Name

func (h *UserClassHandle) Name() string

Name returns the ObjC class name.

func (*UserClassHandle) NewInstance

func (h *UserClassHandle) NewInstance() unsafe.Pointer

NewInstance allocates and initialises an instance of the user class. Returns a +1-retained pointer; the caller must call Track or Release when done.

type WeakRef

type WeakRef[T Object] struct {
	// contains filtered or unexported fields
}

WeakRef holds a type-safe weak reference to an ObjC object. Unlike a strong reference, WeakRef does not increment the retain count. When the referenced object is deallocated the ObjC runtime automatically zeroes the stored pointer, causing GetPtr to return nil.

WeakRef is the correct pattern for delegate and observer scenarios where the delegate holds a back-reference to its owner, avoiding retain cycles.

Usage:

weak := objc.NewWeakRef[*appkit.NSView](myView)

// Later — retrieve and use (non-nil means the object is still alive):
if ptr := weak.GetPtr(); ptr != nil {
    view := appkit.NewNSView(ptr) // constructor takes ownership of the +1 retain
    view.SetNeedsDisplay(ctx, true)
}

func NewWeakRef

func NewWeakRef[T Object](obj T) WeakRef[T]

NewWeakRef creates a weak reference to obj. The obj argument must be non-nil and must carry a non-nil ObjC pointer; a zero WeakRef is returned otherwise.

func (*WeakRef[T]) Clear

func (w *WeakRef[T]) Clear()

Clear removes the weak reference registration, freeing the slot. After Clear, GetPtr always returns nil. Safe to call multiple times.

func (WeakRef[T]) GetPtr

func (w WeakRef[T]) GetPtr() unsafe.Pointer

GetPtr returns the raw ObjC pointer of the referenced object with a +1 retain (via objc_loadWeakRetained), or nil if the object was deallocated.

The caller must transfer ownership of the +1 retain to an object wrapper that registers a Go finalizer. The idiomatic pattern is to pass the pointer directly to the framework constructor (which calls objc.Track):

if ptr := weak.GetPtr(); ptr != nil {
    view := appkit.NewNSView(ptr) // NewNSView calls objc.Track, taking the +1
    view.SetNeedsDisplay(ctx, true)
}

If the pointer is not passed to a constructor, call objc.Release(ptr) to balance the retain.

func (WeakRef[T]) IsAlive

func (w WeakRef[T]) IsAlive() bool

IsAlive reports whether the referenced object is still alive. This is an advisory check — the object may be deallocated between an IsAlive call and the subsequent GetPtr call. Prefer a nil-check on GetPtr for correctness in concurrent code.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL