Turning Sporadic iOS Wild‑Pointer Crashes into Deterministic Failures
This article explains how to convert rare, non‑reproducible Obj‑C wild‑pointer crashes into consistently repeatable crashes by delaying memory release, retaining freed memory in a custom queue, and safely managing it under memory‑pressure conditions.
Continuing from the previous discussion on locating Obj‑C wild‑pointer random crashes, this article shows how to make a non‑deterministic crash become deterministic.
Previously we filled the memory about to be freed with 0x55 so that accessing a wild pointer would more likely crash, but the filled memory can be overwritten by other allocations, keeping the crash probability low.
To increase the crash rate we avoid actually freeing the memory. Instead of calling free, we keep the memory in a custom queue and only release parts of it when the retained size exceeds a threshold or when the system issues a memory‑warning.
UIView* testObj=[[UIView alloc] init];
[testObj release];
for (int i=0; i<10; i++) {
UIView* testView=[[[UIView alloc initWithFrame:CGRectMake(0,200,CGRectGetWidth(self.view.bounds), 60)] autorelease];
[self.view addSubview:testView];
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:nil];
}
[testObj setNeedsLayout];The core implementation uses a thread‑safe queue ( DSQueue) to store pointers to the retained memory. When the queue length or total retained size exceeds predefined limits, a batch of memory is released. The safe_free function either queues the memory after filling it with 0x55 or releases a batch if limits are reached.
DSQueue* _unfreeQueue=NULL; // queue for retained memory
int unfreeSize=0; // total retained size
#define MAX_STEAL_MEM_SIZE (1024*1024*100)
#define MAX_STEAL_MEM_NUM (1024*1024*10)
#define BATCH_FREE_NUM 100
void free_some_mem(size_t freeNum){
size_t count=ds_queue_length(_unfreeQueue);
freeNum = freeNum>count ? count : freeNum;
for (int i=0; i<freeNum; i++) {
void* p = ds_queue_get(_unfreeQueue);
size_t sz = malloc_size(p);
__sync_fetch_and_sub(&unfreeSize, sz);
orig_free(p);
}
}
void safe_free(void* p){
int cnt = ds_queue_length(_unfreeQueue);
if (cnt>MAX_STEAL_MEM_NUM*0.9 || unfreeSize>MAX_STEAL_MEM_SIZE) {
free_some_mem(BATCH_FREE_NUM);
} else {
size_t sz = malloc_size(p);
memset(p, 0x55, sz);
__sync_fetch_and_add(&unfreeSize, sz);
ds_queue_put(_unfreeQueue, p);
}
}
bool init_safe_free(){
_unfreeQueue = ds_queue_create(MAX_STEAL_MEM_NUM);
orig_free = (void(*)(void*))dlsym(RTLD_DEFAULT, "free");
rebind_symbols1((struct rebinding[]){{"free", (void*)safe_free}}, 1);
return true;
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
free_some_mem(1024*1024);
}Key points to remember:
Avoid using locked functions inside safe_free to prevent deadlocks.
The approach increases the app’s memory footprint; it must never be shipped in production.
On devices like iPhone 5 the performance impact is acceptable, but multithreaded scenarios may increase crash rates.
By retaining more memory, the probability that a wild pointer accesses overwritten data rises, making crashes easier to reproduce and debug.
In theory, the larger the retained memory, the higher the crash probability.
Finally, the article encourages developers to share their own crash‑reproduction tricks to improve overall development quality.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
