我的应用程序正在使用Apple提供的UITextChecker
类。这个类有以下错误:如果iOS设备离线(预计通常是我的应用程序),每次我调用一些UITextChecker
方法时,它会记录到控制台:
2016-03-08 23:48:02.119 HereIsMyAppName [4524:469877] UITextChecker sent string:isExemptFromTextCheckerWithCompletionHandler: to com.apple.TextInput.rdt but received error Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.TextInput.rdt was invalidated."
我不希望此邮件发送垃圾邮件。有没有办法从代码中禁用日志记录?我会在调用任何UITextChecker
方法之前禁用日志记录,然后重新启用它。或者是否有任何方法可以选择性地禁用每个类的日志记录(如果基础类不是我的,则为事件)?还是其他任何解决方案?
答案 0 :(得分:9)
警告:此答案使用私有但尚未记录的Cocoa C函数_NSSetLogCStringFunction()
和_NSLogCStringFunction()
。
_NSSetLogCStringFunction()
是Apple创建的用于处理NSLog
的实现功能的界面。它最初是为WebObjects公开,以挂钩到Windows机器上的NSLog
语句,但仍然存在于今天的iOS和OS X API中。它记录在this支持文章中。
该函数接收带有参数const char* message
的函数指针,要记录的字符串,unsigned length
,消息的长度以及切换标准日志记录横幅的BOOL withSysLogBanner
。如果我们为日志创建我们自己的钩子函数实际上没有做任何事情(一个空的实现,而不是像fprintf
那样调用NSLog
做幕后的事情),我们可以有效地禁用你的所有日志记录应用
Objective-C示例(或带有桥接头的Swift):
extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL));
static void hookFunc(const char* message, unsigned length, BOOL withSysLogBanner) { /* Empty */ }
// Later in your application
_NSSetLogCStringFunction(hookFunc);
NSLog(@"Hello _NSSetLogCStringFunction!\n\n"); // observe this isn't logged
可以在YILogHook中找到此示例实现,它提供了一个接口,用于向任何NSLog
语句添加块数组(写入文件等)。
Pure Swift示例:
@asmname("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 @asmname has been renamed to @_silgen_name
func _NSSetLogCStringFunction(_: ((UnsafePointer<Int8>, UInt32, Bool) -> Void)) -> Void
func hookFunc(message: UnsafePointer<Int8>, _ length: UInt32, _ withSysLogBanner: Bool) -> Void { /* Empty */ }
_NSSetLogCStringFunction(hookFunc)
NSLog("Hello _NSSetLogCStringFunction!\n\n"); // observe this isn't logged
在Swift中,您也可以选择忽略所有块参数而不使用hookFunc
,如下所示:
_NSSetLogCStringFunction { _,_,_ in }
要使用Objective-C重新开启记录,只需传入NULL
作为函数指针:
_NSSetLogCStringFunction(NULL);
使用Swift的东西有点不同,因为如果我们尝试传入nil
或nil
指针(NULL
在Swift中不可用),编译器会抱怨类型不匹配。为了解决这个问题,我们需要访问另一个系统函数_NSLogCStringFunction
,以获取指向默认日志记录实现的指针,在禁用日志记录时保留该引用,并在我们想要重新开启日志记录时设置引用。
我通过添加NSLogCStringFunc
typedef
来清理Swift的实现:
/// Represents the C function signature used under-the-hood by NSLog
typealias NSLogCStringFunc = (UnsafePointer<Int8>, UInt32, Bool) -> Void
/// Sets the C function used by NSLog
@_silgen_name("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 @asmname has been renamed to @_silgen_name
func _NSSetLogCStringFunction(_: NSLogCStringFunc) -> Void
/// Retrieves the current C function used by NSLog
@_silgen_name("_NSLogCStringFunction")
func _NSLogCStringFunction() -> NSLogCStringFunc
let logFunc = _NSLogCStringFunction() // get function pointer to the system log function before we override it
_NSSetLogCStringFunction { (_, _, _) in } // set our own log function to do nothing in an anonymous closure
NSLog("Observe this isn't logged.");
_NSSetLogCStringFunction(logFunc) // switch back to the system log function
NSLog("Observe this is logged.")