NSLog实际上是做什么的?

3
我遇到了一个奇怪的问题。我在我的应用程序中使用了苹果私有框架中的一个方法。当我第一次调用它时,它可以正常工作。但是,如果我在没有任何其他操作的情况下立即第二次调用它,它就会崩溃。然而,如果我在两次调用之间放置NSLog,它就能够正常工作。所以我尝试在它们之间放置for循环、sleep()、printf("...")和fprintf(stderr, "...")来模拟NSLog,但这并没有帮助。我想知道这个方法是如何知道我使用了NSLog的?换句话说,NSLog到底对方法的行为产生了什么影响?
非常感谢!
编辑:
我似乎解决了这个问题。我将我的解决方案分享在这里,希望对某些人有用。
我正在使用MultitouchSupport.framework创建一个多点触控相关的应用程序。我从http://aladino.dmi.unict.it/?a=multitouch复制了代码,并在循环的结尾添加了CFRelease。因此,基本上我的主要方法看起来像这样:
int main(void) { 
    int i; 
    NSMutableArray* deviceList = (NSMutableArray*)MTDeviceCreateList(); //grab our device list 
    for(i = 0; i<[deviceList count]; i++) { //iterate available devices 
        MTRegisterContactFrameCallback([deviceList objectAtIndex:i], touchCallback); //assign callback for device 
        MTDeviceStart([deviceList objectAtIndex:i], 0); //start sending events 
    }
    CFRelease((CFMutableArrayRef)deviceList); 
    printf("Ctrl-C to abort\n"); 
    sleep(-1); 
    return 0; 
}

运行一段时间后,会显示“程序收到信号:‘EXC_BAD_ACCESS’”。

以下是堆栈跟踪信息:

#0 0x7fff8795496e in ParsedMultitouchFrameRepInitialize
#1 0x7fff879565b1 in mt_HandleMultitouchFrame
#2 0x7fff87955a03 in mt_DequeueDataFromDriver
#3 0x7fff87955b29 in mt_DequeueMultitouchDataFromDriverThreadEntry
#4 0x7fff831b3456 in _pthread_start
#5 0x7fff831b3309 in thread_start

然而,如果我将NSLog放在MTDeviceStart下面,它就不会崩溃。
我在原始代码中添加CFRelease((CFMutableArrayRef)deviceList)的原因是,我认为从*Create*或*Copy*命名的函数创建的对象应该由我们自己释放。但事实证明,如果像原始代码一样删除它,即使不使用NSLog,它也不会崩溃。
所以,也许是因为我释放了deviceList太早?但如果是这样,为什么NSLog似乎能够防止崩溃呢?

5
可能与NSLog无关,我会发布一些代码。 - thyrgle
请将代码和堆栈跟踪编辑到您的问题中。 - Peter Hosey
你对命名规范的理解是正确的,但由于MTDeviceCreateList是一个私有函数,使用它可能会违反或不符合规范。(也许它的意思是“创建设备数组,该数组旨在在整个进程的生命周期内保持活动状态,并返回指向它的指针”)。尝试在Instruments的Zombies工具下运行程序(带崩溃),这样你就可以证明你的释放是过度释放,或者确定崩溃的真正原因。 - Peter Hosey
我猜 MT 函数不会保留您传递给它们的对象,因此如果您释放设备列表,则这些对象也会过早消失。 - Georg Fritzsche
在 iPhone OS 3 的“早期”,我读了一本书,作者在 SDK 出现之前就学会了为 iPhone 编写代码(记不得他的名字;O'Reilly 出版的书)。他提到私有 API 是多么混乱无意义,而公共 API 则相对有序和优雅(至少当时是这样)。不知道现在是否仍然如此,或者苹果是否已经“清理”了一些东西;我从未使用过任何 jb/私有 API。 - Nicolas Miari
显示剩余2条评论
4个回答

2

类似于这样的内容:

static inline void NSLogMessageString(NSString *string){
  NSString *date=[[NSDate date]
   descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S.%F"
                        timeZone:nil locale:nil];
  NSString *process=[[NSProcessInfo processInfo] processName];

  NSLogFormat(@"%@ %@[%d:%lx] %@",date,process,NSPlatformProcessID(),NSPlatformThreadID(),string);
}

void NSLogv(NSString *format,va_list arguments) {
  NSString *string=NSStringNewWithFormat(format,nil,arguments,NULL);

  NSLogMessageString(string);

 [string release];
}

void NSLog(NSString *format,...) {
  va_list arguments;

  va_start(arguments,format);

  NSLogv(format,arguments);
}

感谢您提出这个问题,哈哈,我想重写它以添加调试变量,这意味着当需要时可以关闭所有NSLogging调用。


1

NSLog可能会影响你遇到的问题,因为它会影响线程执行的顺序。当你在后台线程中调用NSLog时,它必须获得对stdout的独占访问权。使用printf进行线程调试通常会导致“海森堡式错误”(即当你尝试检查它们时,它们会改变行为)。


1

它需要很长时间。我不确定为什么。它会打印日期/时间、进程名称、进程ID、线程ID,最后是您要求的字符串。我认为它也将日志消息发送到syslogd(无论是Xcode还是iPCU的控制台都会将多行NSLog显示为单个条目;我忘记了哪个);那里的IPC可能是重要的。

尝试使用syslog()(#import <syslog.h>然后syslog(LOG_INFO, "Hello there!");,如果它可以工作但没有输出,请尝试更改优先级(参见man 3 syslog)。


是的,NSLog会像syslog一样将消息发送到ASL。我不确定您希望syslog有什么不同的表现方式 - 只是不要将其打印到stderr吗? - Peter Hosey
我在回应“如果我在两个调用之间放置NSLog,它就可以工作”的说法,这表明syslog()可能会执行一些NSLog所做的操作,以使其“工作”。 - tc.

0

这可能是与内存管理有关的问题:可能是多余的释放。如果您发布回溯信息,它可能有助于追踪问题。(事实证明,我关注的Twitter上的某个人昨晚提到了类似的问题)。


哇!非常感谢。我尝试从我的代码中删除了一个CFRelease,现在它完美地工作了。虽然我不确定原因。我已经将我的代码添加到了我的问题中。 - ifvc
请注意:如果它被过早地发布,但最终仍需要发布,这意味着现在它不会崩溃,而是泄漏(即,如果这是一个时间问题,并且您从一开始就正确处理了保留计数)。 - Nicolas Miari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接