Assertion failure in -[UIApplication _runWithMainScene:transitionContext:completion:],

更新Xcode7之后报错:

Assertion failure in -[UIApplication _runWithMainScene:transitionContext:completion:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3491.2.5/UIApplication.m:3173


原因:新的SDK不允许在设置rootViewController之前做过于复杂的操作,导致在didFinishLaunchingWithOptions 结束后还没有设置rootViewController。


解决办法:

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.window makeKeyAndVisible];
之后直接加入代码

    UIViewController* vc = [[UIViewController alloc] initWithNibName:nil bundle:nil];
    self.window.rootViewController = vc;

临时设置一个root,在之后重新对root赋值!
<think>根据您提供的错误信息,程序在`pal_mem_free`函数中发生了断言失败,这通常意味着内存管理出现了问题,比如双重释放、使用已释放内存或内存越界等。 结合之前的修改,我们主要修改了`filename_completion_matches`函数。在这个函数中,我们使用了静态变量来缓存匹配列表。但是,当输入变化时,我们会释放旧资源并重新生成。然而,问题可能出现在: 1. 在释放旧资源时,没有正确释放每一个字符串和数组。 2. 在重新分配内存时,可能出现了错误。 另外,注意`filename_completion_matches`函数返回的数组格式:它应该是一个以NULL结尾的字符串数组。但是,在返回之前,我们添加了NULL终止符,这应该是正确的。 但是,错误发生在`pal_mem_free`,这提示我们在某个内存释放操作中出现了问题。我们需要检查内存分配和释放的配对情况。 我们重新检查`filename_completion_matches`函数中的内存管理: 1. 当输入变化时,我们释放旧的`last_text`和`match_list`(包括其中的每个字符串)。 2. 然后,我们为新的输入生成匹配列表:循环调用`genfunc`(即`filename_completion_function`)直到返回NULL,将每个返回的字符串指针存入数组。 3. 最后,为数组添加一个NULL终止符。 在`filename_completion_function`中,每次返回的字符串都是通过`XMALLOC`分配的,所以我们在`match_list`中释放它们是合理的。 但是,注意:`filename_completion_function`在每次调用时都会返回一个动态分配的字符串,而`filename_completion_matches`函数在收集这些字符串后,会在下次输入变化时释放它们。这看起来是合理的。 然而,问题可能出现在:当`filename_completion_matches`函数被调用时,它返回的数组会被上层调用者使用,然后上层调用者可能会释放这个数组(包括数组中的每个字符串)。但是,我们的函数内部已经缓存了这些字符串,并且在下一次输入变化时释放它们。这就可能导致双重释放。 因此,我们需要明确:`filename_completion_matches`函数返回的数组及其中的字符串,其所有权转移给了调用者,还是由该函数自己管理? 根据readline库的惯例,`filename_completion_matches`函数返回的数组及其中的字符串,其所有权属于调用者。调用者负责释放整个数组(包括数组中的每个字符串)。 但是,我们的实现中,使用静态变量缓存了这些字符串,并且在下一次调用时(输入变化)会释放它们。这就会导致: - 第一次调用返回的数组,如果调用者释放了其中的字符串,那么当我们下一次调用(输入变化)时,我们再次释放这些字符串,就会导致双重释放。 因此,我们需要改变策略:不再缓存字符串,或者缓存但避免重复释放。 解决方案: 1. 每次调用`filename_completion_matches`都重新生成匹配列表,不缓存。这样每次返回的都是新分配的内存,调用者负责释放。 2. 或者,我们让`filename_completion_matches`函数不缓存匹配列表,而是每次调用都重新生成并返回一个新的列表。这样调用者每次都会得到一个新的列表,并负责释放。 考虑到性能,对于大型目录,重新生成可能会慢,但通常目录中的文件数量不会太多。而且,readline库的补全函数通常也不会被频繁调用(只有按Tab时才会调用)。 因此,我们修改为不缓存,每次调用都重新生成匹配列表,并返回给调用者。调用者负责释放返回的整个数组(包括里面的字符串)。 修改后的`filename_completion_matches`函数: ```c char** filename_completion_matches(const char* text, CPFunction* genfunc) { // 收集所有匹配项 char **match_list = NULL; int count = 0; char *match; // 循环调用生成函数直到返回NULL for (int state = 0; (match = (*genfunc)(text, state)) != NULL; state++) { // 扩展数组 char **new_list = (char **)XREALLOC(MTYPE_TMP, match_list, (count + 1) * sizeof(char*)); if (!new_list) { // 分配失败,释放已收集的 for (int i = 0; i < count; i++) { XFREE(MTYPE_TMP, match_list[i]); } XFREE(MTYPE_TMP, match_list); return NULL; } match_list = new_list; match_list[count] = match; count++; } // 添加NULL终止符 if (match_list) { char **new_list = (char **)XREALLOC(MTYPE_TMP, match_list, (count + 1) * sizeof(char*)); if (!new_list) { // 处理分配失败 for (int i = 0; i < count; i++) { XFREE(MTYPE_TMP, match_list[i]); } XFREE(MTYPE_TMP, match_list); return NULL; } match_list = new_list; match_list[count] = NULL; // 终止符 } else { // 如果没有匹配项,返回一个只包含NULL的数组 match_list = (char **)XMALLOC(MTYPE_TMP, sizeof(char*)); if (match_list) { match_list[0] = NULL; } } #ifdef DEBUG printf("\nfilename_completion_matches: Found %d matches for '%s':\n", count, text); for (int i = 0; i < count; i++) { printf(" [%d] %s\n", i, match_list[i]); } #endif return match_list; } ``` ### 关键修改 1. **移除静态变量**:不再缓存匹配列表,每次调用都重新生成。 2. **内存所有权**:返回的数组及其中的字符串由调用者负责释放。 3. **错误处理**:在每次内存分配失败时,释放已分配的资源。 ### 调用者如何释放 上层调用者(readline库)在完成补全操作后,会释放返回的数组。释放方式应该是: ```c char **matches = ...; // 由filename_completion_matches返回 if (matches) { for (int i = 0; matches[i]; i++) { free(matches[i]); // 或使用对应的XFREE } free(matches); } ``` ### 注意事项 1. **内存分配一致性**:确保使用相同的分配和释放函数(例如,如果使用`XMALLOC`分配,则使用`XFREE`释放)。 2. **性能**:对于包含大量文件的目录,可能会产生性能问题。如果遇到这种情况,可以考虑限制返回的匹配项数量(例如,只返回前100个)。 3. **调试**:启用调试输出以确认匹配项的正确性。 ### 完整代码示例 ```c char** filename_completion_matches(const char* text, CPFunction* genfunc) { char **match_list = NULL; int count = 0; char *match; // 循环调用生成函数 for (int state = 0; ; state++) { match = (*genfunc)(text, state); if (match == NULL) { break; } // 扩展数组 char **new_list = (char **)XREALLOC(MTYPE_TMP, match_list, (count + 1) * sizeof(char*)); if (!new_list) { // 分配失败,释放已收集的 for (int i = 0; i < count; i++) { XFREE(MTYPE_TMP, match_list[i]); } XFREE(MTYPE_TMP, match_list); return NULL; } match_list = new_list; match_list[count] = match; count++; } // 处理匹配项 if (count > 0) { // 添加NULL终止符 char **new_list = (char **)XREALLOC(MTYPE_TMP, match_list, (count + 1) * sizeof(char*)); if (!new_list) { for (int i = 0; i < count; i++) { XFREE(MTYPE_TMP, match_list[i]); } XFREE(MTYPE_TMP, match_list); return NULL; } match_list = new_list; match_list[count] = NULL; } else { // 没有匹配项,返回一个只包含NULL的数组 if (match_list) { XFREE(MTYPE_TMP, match_list); } match_list = (char **)XMALLOC(MTYPE_TMP, sizeof(char*)); if (match_list) { match_list[0] = NULL; } } return match_list; } ``` ### 调试输出 在函数中,我们添加了调试输出,这样可以帮助确认匹配项的数量和内容。 ### 错误预防 - 在每次内存分配后检查是否成功。 - 在分配失败时,清理已分配的资源。 §§相关问题§§: 1. 如何避免在文件补全函数中的内存泄漏? 2. 在补全函数中,如何处理包含特殊字符(如空格)的文件名? 3. 如何为文件补全添加忽略某些文件或目录的功能? 4. 在大型目录中,如何实现分页或增量加载以提高性能?</think>根据您提供的错误信息,问题出在内存管理上。断言失败 `pal_mem_free: Assertion `!PAL_MEM_IS_CORRUPT_POINTER(p)' failed` 表明存在内存损坏或双重释放问题。让我提供一个更安全、更健壮的实现方案。 ### 修复后的完整实现 ```c char** filename_completion_matches(const char* text, CPFunction* genfunc) { // 移除所有静态变量 - 避免缓存导致的内存问题 char **match_list = NULL; int count = 0; char *match; // 收集所有匹配项 for (int state = 0; ; state++) { match = (*genfunc)(text, state); if (match == NULL) { break; } // 扩展数组 char **new_list = (char **)XREALLOC(MTYPE_TMP, match_list, (count + 1) * sizeof(char*)); if (!new_list) { // 分配失败,释放已收集的 for (int i = 0; i < count; i++) { XFREE(MTYPE_TMP, match_list[i]); } XFREE(MTYPE_TMP, match_list); return NULL; } match_list = new_list; match_list[count] = match; count++; } // 添加NULL终止符 if (count > 0) { char **new_list = (char **)XREALLOC(MTYPE_TMP, match_list, (count + 1) * sizeof(char*)); if (!new_list) { for (int i = 0; i < count; i++) { XFREE(MTYPE_TMP, match_list[i]); } XFREE(MTYPE_TMP, match_list); return NULL; } match_list = new_list; match_list[count] = NULL; } else { // 没有匹配项,返回一个只包含NULL的数组 match_list = (char **)XMALLOC(MTYPE_TMP, sizeof(char*)); if (match_list) { match_list[0] = NULL; } } #ifdef DEBUG printf("\nfilename_completion_matches: Found %d matches for '%s':\n", count, text); for (int i = 0; i < count; i++) { printf(" [%d] %s\n", i, match_list[i]); } #endif return match_list; } ``` ### 关键修复点 1. **移除静态变量**: - 之前的实现使用了静态变量缓存结果,这可能导致内存管理问题 - 新实现每次调用都重新生成匹配列表,避免缓存相关的内存问题 2. **安全的内存管理**: ```c // 分配失败时的清理 if (!new_list) { for (int i = 0; i < count; i++) { XFREE(MTYPE_TMP, match_list[i]); } XFREE(MTYPE_TMP, match_list); return NULL; } ``` 3. **正确处理空结果**: ```c if (count > 0) { // 添加NULL终止符 } else { // 返回一个只包含NULL的数组 match_list = (char **)XMALLOC(MTYPE_TMP, sizeof(char*)); if (match_list) { match_list[0] = NULL; } } ``` 4. **更健壮的循环**: ```c for (int state = 0; ; state++) { match = (*genfunc)(text, state); if (match == NULL) { break; } // 处理匹配项... } ``` ### 内存管理最佳实践 1. **分配与释放配对**: - 确保每次 `XALLOC` 都有对应的 `XFREE` - 在循环中释放资源时,避免重复释放 2. **指针有效性检查**: ```c // 在释放前检查指针有效性 if (ptr && !PAL_MEM_IS_CORRUPT_POINTER(ptr)) { XFREE(MTYPE_TMP, ptr); } ``` 3. **调试内存问题**: ```c // 在可能的内存问题点添加调试输出 #define DEBUG_MEM 1 void* safe_malloc(size_t size, const char* file, int line) { void* ptr = malloc(size); #if DEBUG_MEM printf("Allocated %p at %s:%d\n", ptr, file, line); #endif return ptr; } void safe_free(void* ptr, const char* file, int line) { #if DEBUG_MEM printf("Freeing %p at %s:%d\n", ptr, file, line); #endif free(ptr); } #define XMALLOC(size) safe_malloc(size, __FILE__, __LINE__) #define XFREE(ptr) safe_free(ptr, __FILE__, __LINE__) ``` ### 完整的调试建议 在 `filename_completion_function` 中添加更多调试信息: ```c char* filename_completion_function(const char* text, int state) { // ... [之前的代码] ... #ifdef DEBUG printf("filename_completion_function: state=%d, text='%s'\n", state, text); printf(" dirname='%s', filename='%s'\n", dirname, filename); #endif // ... [处理目录遍历] ... while ((entry = readdir(dir)) != NULL) { #ifdef DEBUG printf(" Checking: %s\n", entry->d_name); #endif // ... [匹配逻辑] ... if (match_found) { #ifdef DEBUG printf(" MATCH: %s -> %s\n", entry->d_name, temp); #endif return temp; } } // ... [清理代码] ... } ``` ### 错误处理增强 在内存操作周围添加保护: ```c // 安全的字符串复制 char* safe_strdup(const char* src) { if (!src) return NULL; size_t len = strlen(src) + 1; char* dest = XMALLOC(MTYPE_TMP, len); if (dest) { strncpy(dest, src, len); } return dest; } // 安全的字符串连接 char* safe_strcat(char* dest, const char* src) { if (!dest || !src) return dest; size_t dest_len = strlen(dest); size_t src_len = strlen(src); char* new_dest = XREALLOC(MTYPE_TMP, dest, dest_len + src_len + 1); if (new_dest) { strcat(new_dest + dest_len, src); return new_dest; } return dest; } ``` ### 最终建议 1. **启用完整调试**: ```bash # 编译时添加调试标志 gcc -DDEBUG -g -o your_program your_source.c ``` 2. **使用内存检查工具**: ```bash valgrind --leak-check=full --show-leak-kinds=all ./your_program ``` 3. **逐步测试**: - 先测试简单目录(少量文件) - 逐步增加复杂度(嵌套目录、特殊字符文件名) - 测试边界情况(空目录、不存在的目录)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值