6

我有一个使用UITextCheckerApple 提供的类的应用程序。此类有以下错误:如果 iOS 设备处于离线状态(预计对于我的应用程序来说经常是这样),则每次我调用一些UITextCheckers 方法时,它都会将以下内容记录到控制台:

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."

我不想让日志被这些消息发送垃圾邮件。有没有办法从代码中禁用日志记录?我会在调用任何UITextCheckers 方法之前禁用日志记录,然后再重新启用它。或者是否有任何方法可以选择性地禁用每个类的日志记录(如果它是基础类而不是我的事件)?或者任何其他解决方案?

4

1 回答 1

10

警告:这个答案使用私有但排序的 Cocoa C 函数_NSSetLogCStringFunction()_NSLogCStringFunction().

_NSSetLogCStringFunction()是 Apple 创建的一个接口,用于处理NSLog. 它最初是为 WebObjects 与NSLogWindows 机器上的语句挂钩而公开的,但今天仍然存在于 iOS 和 OS X API 中。它记录在支持文章中。

该函数接收一个带有参数的函数指针、const char* message要记录的字符串、unsigned length消息的长度和BOOL withSysLogBanner切换标准日志记录横幅的 a。如果我们创建自己的日志钩子函数,它实际上不做任何事情(一个空的实现而不是fprintfNSLog在幕后那样调用),我们可以有效地禁用您的应用程序的所有日志记录。

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语句(写入文件等)。

纯 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);

nil使用 Swift 时情况略有不同,因为如果我们尝试传入或nil指针(NULL在 Swift 中不可用),编译器会抱怨类型不匹配。为了解决这个问题,我们需要访问另一个系统函数,_NSLogCStringFunction以获取指向默认日志记录实现的指针,在禁用日志记录时保留该引用,并在我们想要重新打开日志记录时设置该引用。

我已经通过添加一个来清理这个的 Swift 实现NSLogCStringFunc typedef

/// 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.")
于 2016-03-16T16:57:28.023 回答