我正在使用 Crashlytics 编写一个错误记录器,我遇到了一个问题,这让我质疑我对协议(protocol)和动态调度的理解。
当使用 Crashlytics 记录非 fatal error 时,API 需要一个符合错误的对象和一个可选的用户信息字典。我目前正在查看 JSON 解码错误,当我刚刚在 recordError 中发送 DecodingError 时,我对在 Crashlytics 仪表板中看到的内容不太满意。所以我的解决方案是为采用 CustomNSError 的 DecodingError 编写一个扩展,以提供一些更详细的信息以帮助将来进行调试:
extension DecodingError: CustomNSError {
public static var errorDomain: String {
return "com.domain.App.ErrorDomain.DecodingError"
}
public var errorCode: Int {
switch self {
case .dataCorrupted:
return 1
case .keyNotFound:
return 2
case .typeMismatch:
return 3
case .valueNotFound:
return 4
}
}
public var errorUserInfo: [String : Any] {
switch self {
case .dataCorrupted(let context):
var userInfo: [String: Any] = [
"debugDescription": context.debugDescription,
"codingPath": context.codingPath.map { $0.stringValue }.joined(separator: ".")
]
guard let underlyingError = context.underlyingError else { return userInfo }
userInfo["underlyingErrorLocalizedDescription"] = underlyingError.localizedDescription
userInfo["underlyingErrorDebugDescription"] = (underlyingError as NSError).debugDescription
userInfo["underlyingErrorUserInfo"] = (underlyingError as NSError).userInfo.map {
return "\($0.key): \(String(describing: $0.value))"
}.joined(separator: ", ")
return userInfo
case .keyNotFound(let codingKey, let context):
return [
"debugDescription": context.debugDescription,
"codingPath": context.codingPath.map { $0.stringValue }.joined(separator: "."),
"codingKey": codingKey.stringValue
]
case .typeMismatch(_, let context), .valueNotFound(_, let context):
return [
"debugDescription": context.debugDescription,
"codingPath": context.codingPath.map { $0.stringValue }.joined(separator: ".")
]
}
}
}
我在记录器中编写了一个方法,如下所示:
func log(_ error: CustomNSError) {
Crashlytics.sharedInstance().recordError(error)
}
然后我将错误发送到这里:
do {
let decoder = JSONDecoder()
let test = try decoder.decode(SomeObject.self, from: someShitJSON)
} catch(let error as DecodingError) {
switch error {
case .dataCorrupted(let context):
ErrorLogger.sharedInstance.log(error)
default:
break
}
}
但是传递给 log(_ error:) 的对象不是我对 CustomNSError 的实现,它看起来像一个带有 NSCocoaErrorDomain 的标准 NSError。
我希望这足够详细来解释我的意思,但不确定为什么传递给日志的对象没有我在 DecodingError 的扩展中设置的值。我知道我可以轻松地在调用 Crashlytics 时单独发送额外的用户信息,但我很想知道我对这种情况的理解哪里出了问题。
最佳答案
NSError 桥接是 Swift 编译器中一个有趣的野兽。一方面,NSError 来自 Foundation 框架,您的应用程序可能会或可能不会使用它;另一方面,实际的桥接机制需要在编译器中执行,并且正确地,编译器应该尽可能少地了解标准库之上的“高级”库。
因此,编译器对 NSError 到底是什么知之甚少,相反,Error exposes three properties它提供了 NSError 的全部底层表示:
public protocol Error {
var _domain: String { get }
var _code: Int { get }
// Note: _userInfo is always an NSDictionary, but we cannot use that type here
// because the standard library cannot depend on Foundation. However, the
// underscore implies that we control all implementations of this requirement.
var _userInfo: AnyObject? { get }
// ...
}
NSError,然后,有一个 Swift extension which conforms to Error and implements those three properties :
extension NSError : Error {
@nonobjc
public var _domain: String { return domain }
@nonobjc
public var _code: Int { return code }
@nonobjc
public var _userInfo: AnyObject? { return userInfo as NSDictionary }
// ...
}
有了这个,当你import Foundation时,任何Error都可以转换为NSError,反之亦然,因为两者都公开了 _domain、_code 和 _userInfo(编译器实际使用它来执行桥接)。
CustomNSError 协议(protocol)通过允许您提供 errorDomain、errorCode 和 errorUserInfo 发挥作用,然后由 various extensions 公开作为他们的下划线版本:
public extension Error where Self : CustomNSError {
/// Default implementation for customized NSErrors.
var _domain: String { return Self.errorDomain }
/// Default implementation for customized NSErrors.
var _code: Int { return self.errorCode }
// ...
}
那么,EncodingError 和 DecodingError 有何不同?好吧,因为它们都是在标准库中定义的(无论您是否使用 Foundation,它都存在,并且不能依赖于 Foundation),它们通过 providing implementations of _domain, _code, and _userInfo directly 连接到系统中。 .
由于这两种类型都提供了这些变量的直接下划线版本,它们不会调用非下划线版本来获取域、代码和用户信息——直接使用这些值(而不是依赖于 var _domain: String { return Self.errorDomain }).
因此,实际上,您无法覆盖该行为,因为 EncodingError 和 DecodingError 已经提供了此信息。相反,如果你想提供不同的代码/域/用户信息字典,你将需要编写一个函数,它接受一个 EncodingError/DecodingError 并返回你自己的NSError,或类似的。
关于swift - 在 DecodingError 中采用 CustomNSError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48344928/
Struct让我创建一个新类,它接受参数并具有一些很好的语义。但是,参数不是必需的,它们的顺序需要引用定义:Point=Struct.new(:x,:y)Point.new(111,222)#=>Point.new(111)#=>我想要类似于Struct的东西,但它使用关键字参数代替:Point=StricterStruct.new(:x,:y)Point.new(x:111,y:222)#=>Point.new(x:111)#=>ArgumentError这可能看起来像这样:moduleStricterStructdefself.new(*attributes)klass=Class
我知道我可以使用define_method在类上动态定义方法,并且我使用block的元数指定此方法采用的参数。我想动态定义一个接受可选参数和block的方法。在Ruby1.9中,这很容易,因为现在允许将block传递给block。不幸的是,Ruby1.8不允许这样做,所以下面的方法将不起作用:#Ruby1.8classXdefine_method:foodo|bar,&baz|putsbarbaz.callifblock_given?endendx=X.newx.foo("foo"){puts"called!"}#=>LocalJumpError:noblockgiven用yield替
脑子有点炸了....我如何从relative_path获取:controller和:action的散列?这基本上与url_for相反。在下面的示例中,“some_function”是我正在寻找的神秘函数名称...我知道这很简单,只是不记得或似乎无法在文档中找到它。像这样:some_function('/posts/1/edit')=>{:controller=>'posts',:action=>'edit',:id=>'1'} 最佳答案 Rspec有一个方法'params_for',它使用ActionController的路由方法将
我有这样的方法:deffoo(fruit='apple',cut="sliced",topping="icecream")#somelogichereend我怎样才能调用它,我只覆盖顶部参数但对其他参数使用默认值,就像这样foo('','','hotfudge')当然这不会按预期工作,但我只想为第三个可选参数提供一个值,并让前两个保持默认值。我知道如何使用散列来做到这一点,但他们是使用上述语法的快捷方式吗? 最佳答案 从Ruby2.0开始,您可以使用关键字参数:deffoo(fruit:'apple',cut:"sliced",to
看起来很简单,但一直无法弄清楚如何让它发挥作用。在模型.rb中:defModelattr_accessor:width,:heightdefinitializeparams@width=params[:width]@height=params[:height]...在工厂文件models.rb中:FactoryGirl.definedofactory:modeldoheight5width7endend在工厂方法中设置属性会抛出错误wrongnumberofarguments(0for1)在没有Rails的情况下使用Ruby1.9.3,使用Factory.build。FactoryGi
1.主要实验设备及器材1.1一块ESP8266(如图1)图1 ESP8266模块1.2一个USB转TTL模块(如图2)图2 USB转TTL模块2.测试ESP8266模块 2.1连接设备 WIFI模块与USB转TTL模块进行连接,连接实物图如图3所示,硬件连线框图如图4所示。图3 连接实物图图4 硬件连接框图2.2打开串口调试助手 本次实验使用的软件是XCOMV2.3,默认波特率为115200,停止位为1,数据位为8,校验位为None(如图5)。图5 XCOMV2.32.3输入测试指令AT 测试AT启动,返回OK(如图6)图6 AT测试2.4复位指令AT
在1.9.2p0中,Date#parse采用UE格式。如果您不相信我,请查看format.rb,第1042行。无论如何,我怎样才能让它采用美国格式,以便:>Date.parse("10/4/2010")=>Mon,04Oct2010而不是4月10日。我已经试过了:classDatedef_parse_eu(str,e)_parse_us(str,e)endend但运气不好。还有其他想法吗? 最佳答案 Date.strptime是你想要的但不幸的是它看起来不像文档有日期格式字符串。我得到以下基于格式字符串的谷歌搜索的工作:1.9.2>
基本的irb测试表明RubyHash以匹配顺序返回.keys和.values。假设是这种情况是否安全? 最佳答案 是的。根据RubyDocsforHash,"哈希按照插入相应键的顺序枚举它们的值。"因此,如果以相同的方式创建哈希,您应该始终获得相同的哈希顺序。 关于RubyHash.keys和.values,可以安全地采用相同的顺序吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/question
有什么方法可以在回调接受两个以上参数的情况下promisify一个函数?一个例子是node的fs.read,回调的三个参数是err、bytes和data。data参数没有传递给then函数,所以这个记录未定义:varfs=require('fs');varPromise=require('bluebird');varopen=Promise.promisify(fs.open);varread=Promise.promisify(fs.read);open('test.txt','r').then(function(fd){varbuffer=newBuffer(1024);read(
根据mustacheRFCA{{name}}taginabasictemplatewilltrytofindthenamekeyinthecurrentcontext.Ifthereisnonamekey,nothingwillberendered.因此我期望这样:vartemplate='{{#anArray}}{{aString}}{{/anArray}}';varjson={"aString":"ABC","anArray":[1,{"aString":"DEF"}]};渲染后给我:"DEF"然而,mustache.js在父级范围内查找值。这给了我"ABCDEF"上下文是否真的意