Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -294,6 +294,14 @@ These are the things we've burned ourselves on. Following them isn't optional.
*the platform API didn't complain*. See
`decisions/2026-08-09-tap-xy-reports-observed-effect.md`.

15. **Never capture `[nsstring UTF8String]` in a block that outlives the
scope.** The pointer belongs to the NSString (and is autorelease-scoped
besides), so a `const char *` captured for a callback — a `UIAlertAction`
handler, a completion block, anything the run loop calls back later — is
reading freed memory by the time it fires. Capture the object and convert
inside the block. Both alert NIFs did this, and it is invisible in testing
because freed bytes usually still spell the old string.

## Where to look

| Question | File |
Expand Down
12 changes: 8 additions & 4 deletions ios/mob_nif.m
Original file line numberDiff line numberDiff line change
Expand Up@@ -7350,11 +7350,16 @@ static ERL_NIF_TERM nif_alert_show(ErlNifEnv *env, int argc, const ERL_NIF_TERM
as = UIAlertActionStyleCancel;
if ([style isEqualToString:@"destructive"])
as = UIAlertActionStyleDestructive;
const char *act_c = [action UTF8String];
// Capture the NSString, not [action UTF8String]. The C pointer aims
// into a string owned by `buttons`, a local ARC releases the moment
// this block returns — long before anyone taps — so the handler read
// freed memory and enif_make_atom built the action atom out of
// whatever was there. Capturing the object retains it for the life
// of the handler.
[ac addAction:[UIAlertAction actionWithTitle:label
style:as
handler:^(UIAlertAction *_) {
mob_deliver_alert_action(act_c);
mob_deliver_alert_action([action UTF8String]);
}]];
}
UIViewController *vc = root_vc();
Expand DownExpand Up@@ -7399,11 +7404,10 @@ static ERL_NIF_TERM nif_action_sheet_show(ErlNifEnv *env, int argc, const ERL_NI
as = UIAlertActionStyleCancel;
if ([style isEqualToString:@"destructive"])
as = UIAlertActionStyleDestructive;
const char *act_c = [action UTF8String];
[ac addAction:[UIAlertAction actionWithTitle:label
style:as
handler:^(UIAlertAction *_) {
mob_deliver_alert_action(act_c);
mob_deliver_alert_action([action UTF8String]);
}]];
}
UIViewController *vc = root_vc();
Expand Down