Mobile Development 9 min read

Investigation of CFAllocatorRef and Custom Allocators for Efficient String Decoding on iOS

The article examines how iOS string decoding failures caused by malformed UTF‑8 bytes can be mitigated by parsing data byte‑by‑byte and using a custom CFAllocatorRef that reuses a fixed‑size stack or pre‑allocated buffer, avoiding heap fragmentation and improving performance.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Investigation of CFAllocatorRef and Custom Allocators for Efficient String Decoding on iOS

When transmitting text to a server, a single character with an unexpected encoding can cause the whole string to fail decoding. If the problematic character can be identified and replaced, the rest of the text can be decoded, improving user experience.

The presented approach parses data byte‑by‑byte when initWithData:encoding: cannot decode it. A snippet of the original source shows a loop that checks 1‑byte, 2‑byte, and longer UTF‑8 sequences, creates a CFString with CFStringCreateWithBytes , and replaces invalid bytes.

while(检索未超过文件长度)
{
    if(1字节长的编码)
    {/*正确编码,继续循环*/}
    else if (2字节长的编码)
    {
        CFStringRef cfstr = CFStringCreateWithBytes(kCFAllocatorDefault, {byte1, byte2}, 2, kCFStringEncodingUTF8, false);
        if (cfstr)
        {/*正确编码,继续循环*/}
        else
        {/*替换字符*/}
    }
    else if(3,4,5,6个字节长的解码)...
}

The drawback of this method is that CFStringCreateWithBytes allocates the string on the heap, which can cause memory fragmentation for large data. Two solutions are proposed: allocate on the stack or reuse a pre‑allocated heap buffer.

CFAllocatorRef allows the caller to specify the memory allocator used by CFStringCreateWithBytes . The allocator is described by the CFAllocatorRef alloc parameter, which can be NULL (default allocator) or a custom allocator.

The CoreFoundation allocator structure is defined as:

typedef const struct CF_BRIDGED_TYPE(id) __CFAllocator * CFAllocatorRef;
struct __CFAllocator {
    CFRuntimeBase _base;
    CFAllocatorRef _allocator;
    CFAllocatorContext _context;
};

On iOS, the __CFAllocator struct contains three members, with CFAllocatorContext _context being the core that holds callbacks for allocation and deallocation:

typedef void * (*CFAllocatorAllocateCallBack)(CFIndex allocSize, CFOptionFlags hint, void *info);
typedef void (*CFAllocatorDeallocateCallBack)(void *ptr, void *info);
typedef struct {
    ...
    CFAllocatorAllocateCallBack allocate;
    CFAllocatorDeallocateCallBack deallocate;
    ...
} CFAllocatorContext;

The system provides several built‑in allocators, each implemented with different underlying functions. For example, kCFAllocatorMalloc uses malloc / realloc / free :

static void * __CFAllocatorCPPMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
{ return malloc(allocSize); }
static void __CFAllocatorCPPFree(void *ptr, void *info)
{ free(ptr); }

Other allocators such as kCFAllocatorMallocZone , kCFAllocatorNull , kCFAllocatorUseContext , kCFAllocatorDefault , and kCFAllocatorSystemDefault are also described, each with its own characteristics.

To avoid heap fragmentation, a custom allocator can be created with CFAllocatorCreate . The following example shows a custom allocator that reuses a fixed‑size buffer supplied via the info field of the context:

void *customAlloc(CFIndex size, CFOptionFlags hint, void *info)
{
    return info;
}
void *customRealloc(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info)
{
    NSLog(@"警告:发生了内存重新分配");
    return NULL;
}
void customDealloc(void *ptr, void *info)
{
    // Do not free; the buffer is managed externally
}
CFAllocatorRef customAllocator(void *address)
{
    CFAllocatorRef allocator = NULL;
    if (NULL == allocator)
    {
        CFAllocatorContext context = {0, NULL, NULL, NULL, NULL, customAlloc, customRealloc, customDealloc, NULL};
        context.info = address;
        allocator = CFAllocatorCreate(kCFAllocatorSystemDefault, &context);
    }
    return allocator;
}
int main()
{
    char allocAddress[160] = {0};
    CFAllocatorRef allocator = customAllocator(allocAddress);
    CFStringRef cfstr = CFStringCreateWithBytes(allocator, tuple, 2, kCFStringEncodingUTF8, false);
    if (cfstr)
    {
        // Do not release; memory is on the stack and reclaimed automatically
    }
    CFAllocatorDeallocate(kCFAllocatorSystemDefault, (void *)allocator);
}

The buffer size (160 bytes in the example) must be at least as large as the maximum length required by CFStringCreateWithBytes . The custom allocator itself resides on the heap and must be released with CFAllocatorDeallocate , using the same allocator that created it.

In conclusion, a custom CFAllocator gives developers control over memory placement, allowing reuse of a fixed buffer to reduce fragmentation and improve performance when decoding strings on iOS.

References:

Apple CoreFoundation source

Gist example

Custom Allocators documentation

QA1419

iOSMemory ManagementCFAllocatorRefCoreFoundationCustom Allocator
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.