__NSCFString appendString: Cocoa 中 NSMutableString 崩溃

Posted

技术标签:

【中文标题】__NSCFString appendString: Cocoa 中 NSMutableString 崩溃【英文标题】:__NSCFString appendString: crash for NSMutableString in Cocoa 【发布时间】:2013-05-10 14:36:19 【问题描述】:

我一直面临这个问题,我承认我缺乏一些内存管理的基本概念。我一直无法解决这个问题并相信我,我已经尝试了很多事情。

在我的应用程序中,有 4 个线程(将来最多可以有 12 个线程)将从 RS232 端口读取。在每个线程 (threadRS232Read) 中,我将使用 appendString 将 RS232 字符附加到相应的 NSMutableString 中。 NSMutableString 将与 appendString 一起变得非常大,直到完成完整的测试。并定期在测试中调用clearRdBuffStr 来清除字符串。该应用程序总是在 appendString 中崩溃。如果幸运的话,我可以运行一些测试,但通常情况下,它会在第一次运行时崩溃。下面是代码 sn-ps 和崩溃日志。

AppController.h
...
`@interface AppController : NSObject 
...
NSMutableString *buffStr1, *buffStr2, *buffStr3, *buffStr4;
...

AppController.m
...
//in -(id)init
buffStr1 = [[NSMutableString alloc] initWithString:@""];
buffStr2 = [[NSMutableString alloc] initWithString:@""];
buffStr3 = [[NSMutableString alloc] initWithString:@""];
buffStr4 = [[NSMutableString alloc] initWithString:@""];
...
// in -(void)dealloc
[buffStr1 release];
[buffStr2 release];
[buffStr3 release];
[buffStr4 release];`

在另一个文件 RS232RW.m 中,将使用一个线程将 buffStr1 更新为 4

RS232RW.m
- (void)threadRS232Read:(id)argument  
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
....
//read from RS232 port buffer and etc...
NSString *buffStr = [NSString stringWithUTF8String:buff];
    switch (portNo) 
        case 0:
            if (buffStr != nil)
            
                [buffStr1 appendString:buffStr];
            

            break;
        case 1:
            if (buffStr != nil)
            
                [buffStr2 appendString:buffStr];
            
            break;
        case 2:
            if (buffStr != nil)
            
                [buffStr3 appendString:buffStr];
            

            break;
        case 3:
            if (buffStr != nil)
            
                [buffStr4 appendString:buffStr];
            
            break;
        case 4:
            if (buffStr != nil)
            
                [buffStr5 appendString:buffStr];
             
            break;

        ....

// clearRdBuffStr will be called by the other part of the program to clear this buffer.
-(void) clearRdBuffStr:(int) portNo 
switch (portNo) 
    case 0:
        [buffStr1 setString:@""];
        break;
    case 1:
        [buffStr2 setString:@""];
        break;
    case 2:
        [buffStr3 setString:@""];
        break;
    case 3:
        [buffStr4 setString:@""];
        break;
    ....

应用程序总是在上面 appendString 的 1 处崩溃。

崩溃日志如下:

   ....


Crashed Thread:  3

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000

Application Specific Information:
objc[584]: garbage collection is OFF
*** error for object 0x6d02b600: double free
....
Thread 3 Crashed:
0   libsystem_kernel.dylib          0x905819c6 __pthread_kill + 10
1   libsystem_c.dylib               0x926ecf78 pthread_kill + 106
2   libsystem_c.dylib               0x926ddbdd abort + 167
3   libsystem_c.dylib               0x92701508 szone_error + 333
4   libsystem_c.dylib               0x92702dd1 free_small_botch + 102
5   com.apple.CoreFoundation        0x97ee51e8 __CFAllocatorSystemDeallocate + 24
6   com.apple.CoreFoundation        0x97ee51ba CFAllocatorDeallocate + 266 
7   com.apple.CoreFoundation        0x97ee50a2 __CFStrDeallocateMutableContents + 178
8   com.apple.CoreFoundation        0x97ee422b __CFStringChangeSizeMultiple + 3147
9   com.apple.CoreFoundation        0x97f86010 __CFStringCheckAndReplace + 496
10  com.apple.CoreFoundation        0x97f95dad -[__NSCFString appendString:] + 45
11  com.TopTestDFU                  0x00109f6d -[AppController(RS232RW) 
threadRS232Read:] + 752
12  com.apple.Foundation            0x92aabf7d -[NSThread main] + 45 
13  com.apple.Foundation            0x92aabf2d __NSThread__main__ + 1582
14  libsystem_c.dylib               0x926eaed9 _pthread_start + 335
15  libsystem_c.dylib               0x926ee6de thread_start + 34

【问题讨论】:

我建议使用 Zombies 模板在 Instruments 下运行该应用程序。如果结果是僵尸(我认为不会,但总是值得检查),Instruments 将帮助您找到原因。如果它不是僵尸,你至少可以在 Instruments 的对象历史中查找双重释放的地址,看看它是什么,是什么创建了它等等,这可能是你需要的线索。 【参考方案1】:

NSMutableString 等 Cocoa 可变对象不是线程安全的。您可以使用某种形式的锁来安排同步。

查看 NSLock 以获得一个选项。

您可以尝试将 4 个 NSMutableStrings 变成原子属性而不是 ivars。在这种情况下它可能会起作用,尽管 atomic 实际上并不能保证线程安全。

[编辑] ...实际上,如果在您的情况下,您确定每个线程都使用不同的端口号,那么问题可能不是 NSMutableStrings 本身。可能NSString *buffStr = [NSString stringWithUTF8String:buff]; 在另一个线程完成附加buffStr 之前被一个线程调用。所以你需要一个锁,以及附加。

【讨论】:

[提问者] 非常感谢您的回复。是的。我确信每个线程都使用不同的端口号。事实上,我之前尝试过使用 NSMutableString 作为属性(复制或保留)和 NSLock,但它仍然像上面那样崩溃。 ...switch (portNo) case 0: if (buff[0] != '\0') [lock_buffStr[portNo] lock]; [buffStr1 appendString:[NSString stringWithUTF8String:buff]]; [lock_buffStr[portNo] unlock]; break; ....

以上是关于__NSCFString appendString: Cocoa 中 NSMutableString 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

NSMutableDictionary 转换成 __ NSCFString

Swift - 无法将“__NSCFString”类型的值转换为“NSDictionary”

__NSCFString objectForKeyedSubscript: 异常

如何解决 [__NSCFString _isResizable] 的运行时错误:无法识别的选择器发送到实例?

集合 <__NSCFString:> 在被枚举崩溃时发生了变异

[__NSCFString objectAtIndex:]:无法识别的选择器发送到实例错误