I am reading MirrorType at nshipster,and trying to adopt it's code to Swift 2.1. Everything works fine until when I tried to custom the _MirrorTypewith :
extension WWDCSession : _Reflectable {
func _getMirror() -> _MirrorType {
return WWDCSessionMirror(self)
}
}
An error occured :
error: Playground execution aborted: Execution was interrupted,
reason: EXC_BAD_ACCESS (code=2, address=0x7fff58273e87).
And I found out it's because the init method in WWDCSessionMirror was being called infinite times.
struct WWDCSessionMirror: _MirrorType {
private let _value: WWDCSession
init(_ value: WWDCSession) {
_value = value
}
var value: Any { return _value }
var valueType: Any.Type { return WWDCSession.self }
var objectIdentifier: ObjectIdentifier? { return nil }
var disposition: _MirrorDisposition { return .Struct }
var count: Int { return 4 }
subscript(index: Int) -> (String, _MirrorType) {
switch index {
case 0:
return ("number", _reflect(_value.number))
case 1:
return ("title", _reflect(_value.title))
case 2:
return ("track", _reflect(_value.track))
case 3:
return ("summary", _reflect(_value.summary))
default:
fatalError("Index out of range")
}
}
var summary: String {
return "WWDCSession \(_value.number) [\(_value.track.rawValue)]: \(_value.title)"
}
var quickLookObject: PlaygroundQuickLook? {
print(summary)
return .Text(summary)
}
}
I want to ask why it happened , and how to fix it?
_Reflectable and _MirrorType are not the droids you're looking for.
They are legacy types, which have been superseded by CustomReflectable (among others). The 2015 WWDC session about LLDB goes into some detail about this (disclaimer: I am the speaker of that part of that session, so conflict of interests all around :-)
But, anyway, the issue you're running into is because of this line:
_value = value
Since you're typing this line in your playground, that tells the playground logic to capture for display ("log" in playground parlance) the thing you're assigning. To do so, the playground uses the Mirror attached to that type. So, we go off and create one, which causes us to run
_value = value
again, which tells the playground logic to log value, which then means we create a Mirror, ...
You should first of all check if you can adopt Mirror and CustomReflectable instead of _MirrorType and if using those APIs fixes your problem. If it doesn't a possible workaround is to put the reflection support code in an auxiliary source file which will cause the playground logic to not log things inside of it.
Related
I've been trying to figure out what is going on to no avail. I've distilled the code as much as possible but I still get the "Result of operator && is unused warning (even though it is used) if I do it in a project but the same code copied to Playground is working fine with no warnings. This is just some dummy code, after me rewriting the basic code again while trying to find the problem.
enum WordError: Error {
case tooShort
case tooLong
}
func isTooShort(_ word: String) throws -> Bool {
if word.count < 3 { throw WordError.tooShort }
return true }
func isTooLong(_ word: String) throws -> Bool {
if word.count > 5 { throw WordError.tooLong }
return true }
func check(_ word: String) {
do {
try isTooShort(word) && isTooLong(word)
print(word)
} catch let error as WordError {
print("\(error)")
} catch {
}
}
Is this just a bug or am I doing something wrong here?
I figured I can silence the warning if I use:
try _ = isTooShort(word) && isTooLong(word)
But I'm not sure whether that's the right way of 'patching' it.
There is nothing wrong with doing it that way. The "right" way, when something like isTooShort belongs to you and you want to call it without capturing the result, is to mark it with #discardableResult. If you did that, then you could write
do {
try isTooShort(word)
try isTooLong(word)
print(word) // if we get here, it's neither too short nor too long
} catch ...
But what you're doing is also "right" in these circumstances.
The real question is why you would both return a Bool and throw an error. Your implementation of isTooShort is very odd. You seem to be misusing throw. It isn't clear what problem you are trying to solve by implementing it in this odd way. isTooShort can only fail one way: the word is too short. So why doesn't it just return a Bool? isTooShort asks a simple yes/no question, so just answer it: return a Bool and stop.
If your goal is to answer a three-way question - i.e., to tell the caller whether this word was too short, too long, or just right, then again, just return a custom enum that answers the question:
enum WordLength {
case tooShort
case tooLong
case justRight
}
func howIs(_ word: String) -> WordLength {
if word.count < 3 { return .tooShort }
if word.count > 5 { return .tooLong }
return .justRight
}
I've got following code
protocol NamedOption {
var optionTitle: String { get }
}
struct DebugOption: NamedOption {
let optionTitle: String
let debugViewControllerType = UIViewController.self
}
func testFunk<T: Sequence>(d: Observable<T>) where T.Element == NamedOption {
}
func bindFullResultsRx() {
let dd: Observable<[DebugOption]> = self.dataModel.debugOptions // this is defined and properly assigned earlier
testFunk(d: dd)
}
and at the line where I call testFunk Xcode gives me following error:
Expression type '()' is ambiguous without more context
I have no idea why this is happening :( So far I was able to make it working by changing constraints on testFunk into this:
func funk<T: NamedOption>(d: Observable<[T]>) {
}
which seems to me more restrictive then version at the top. Does any one knows how to make it working with T: Sequence ?
Xcode version is 9.4, Swift version is 4.1.
After some digging and help from work colleagues I was able to make it working by simply changing == into : so it looks like this
func testFunk<T: Sequence>(d: Observable<T>) where T.Element: NamedOption {
}
It's just a matter of swift syntax
https://docs.swift.org/swift-book/ReferenceManual/GenericParametersAndArguments.html
conformance-requirement → type-identifier : protocol-composition-type
OO and generics don't play too well together. In order to do what you want, you have to manually cast up as in:
testFunk(d: dd.map { $0.map { $0 as NamedOption } })
I wrote very simple piece of code in Swift:
protocol MultiplyByTwoProtocol {
func multiply() -> Int
}
class MultiplyByTwoClass: MultiplyByTwoProtocol {
private let n: Int
init(n: Int) { self.n = n }
func multiply() -> Int { return 2 * n }
}
class DynamicDispatchSwift {
private let items: [MultiplyByTwoProtocol]
init(n: Int) {
self.items = Array<Int>.generate(size: n).map(MultiplyByTwoClass.init)
}
func run() {
items.forEach { input in
_ = input.multiply()
}
}
}
(btw generate method for Array just creates an array of random Ints)
Then, I run that code in Instruments and I got following results:
As you can see, almost half of the time takes entry called outlined init with copy of MultiplyByTwoProtocol. Does anyone know what it is?
Thanks for your help.
I just ran into outlined init with copy of ... as the top of a stack trace for crashes coming from an app built with release configuration. I found that turning off the compiler optimization settings would prevent the crash. Ultimately I also found an equivalent syntax for the code the stack trace pointed to, that the optimizer was having problems with, that did not cause crashes and was able to turn optimization back on.
In my case the line of code causing the issue was accessing a static constant inside a for loop. Assigning that same static constant to nothing just prior to the loop fixed the crash, as in this example:
let _ = Foo.fooConstant // This line fixed the crash
for index in 0..<values.count {
let someBar = Bar(foo: .fooConstant)
...
}
By chance, I discovered that you can do this without the compiler complaining:
extension Date {
var timeIntervalSinceNow: TimeInterval {
return 1000
}
}
What's weirder, is that this actually evaluates to 1000:
Date().timeIntervalSinceNow
The extension seems to hide the original member.
So I tried to do this with my own class:
class A {
var a: String {
return "A"
}
}
extension A {
var a: String {
return "a"
}
}
and it fails to compile: "invalid redeclaration of 'a'".
I observed that this does not affect the usage of the original member through a protocol, which is expected behaviour of hiding:
extension Date {
var description: String {
return "XXXX"
}
}
let date: CustomStringConvertible = Date()
date.description // normal date
Date().description // "XXXX"
Can you explain why does the bullet pointed phenomena occur?
This works because you are declaring this extension in a separate module from the original variable declaration.
Across modules a variable name can be overloaded but in my mind this has been a shortcoming of Swift as there is currently no way to explicitly state which module declaration it is that you want.
I have an app managing a simple stocks portfolio. Amongst other things, it keeps a record of the required exchange rates in a dictionary, like so:
[ EURUSD=X : 1.267548 ]
This disctionary is a Dictionary property of a singleton called CurrencyRateStore.
When updating the stocks quotations, it checks for an updated exchange rate and updates the dictionary with the following code:
CurrencyRateStore.sharedStore()[symbol] = fetchedRate.doubleValue
That calls:
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
The first time the app is started, it works fine.
But if I quit the app (with the Home button) and then relaunch it, the currency rates are updated again, but this time, I get a EXC_BAD_ACCESS at the line
dictionary[index] = newValue!
Here is a screenshot:
[EDIT] Here is the thread in the debug navigator:
I tried to update the dictionary without a subscript, like so:
CurrencyRateStore.sharedStore().dictionary[symbol] = fetchedRate.doubleValue
but without more success. Same if I use the function updateValue:forKey:
I didn't have the issue in Objective-C.
Thanks for your help !
[EDIT] Here is the whole class CurrencyRateStore:
class CurrencyRateStore {
// MARK: Singleton
class func sharedStore() -> CurrencyRateStore! {
struct Static {
static var instance: CurrencyRateStore?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = CurrencyRateStore()
}
return Static.instance!
}
// MARK: Properties
/** Dictionary of currency rates used by the portfolio, presented like [ EURUSD=X : 1.3624 ] */
var dictionary = [String : Double]()
/** Returns a sorted array of all the keys on the currency rates dictionary */
var allKeys: [String] {
var keysArray = Array(dictionary.keys)
keysArray.sort {$0 < $1}
return keysArray
}
init() {
if let currencyRateDictionary: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithFile(currencyRateArchivePath) {
dictionary = currencyRateDictionary as [String : Double]
}
}
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
// (ApplicationWillEnterForeground triggers updateStocks)
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
func deleteRateForKey(key: String) {
dictionary.removeValueForKey(key)
}
/** Removes all currency rates from the Currency rate store */
func deleteAllRates()
{
dictionary.removeAll()
}
// MARK: Archive items in CurrencyRateStore
var currencyRateArchivePath: String { // Archive path
var documentDirectories: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
// Get the only document directory from that list
let documentDirectory: AnyObject = documentDirectories.first!
return documentDirectory.stringByAppendingPathComponent("currencyRates.archive")
}
func saveChanges()-> Bool
{
// return success or failure
return NSKeyedArchiver.archiveRootObject(dictionary, toFile: currencyRateArchivePath)
}
}
This looks to me like a concurrency issue. Swift dictionaries aren't thread safe, and using them from a singleton can lead to multiple reader/writer issues.
Edit: I am pretty sure this is the real answer, based on the given source/debugging dump. To correct what I wrote, specifically MUTABLE dictionaries and arrays (as well as NSMutableDictionary and NSMutableArray) aren't thread safe, and problems arise when using them within Singletons that are accessed from multiple threads, and that appears to be what the sample source code is doing, or enabling other parts of the code to do.
I don't have an Apple link discussing Swift collection class thread safety, but I"m pretty sure common knowledge. But the following tutorial on Grand Central Dispatch discusses the problem in depth and how to solve it using GCD.
http://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1
The error, and the line itself:
dictionary[index] = newValue!
makes me think the problem is newValue being nil - and the error is caused by the forced unwrapping.
I would suggest to set a breakpoint and check its value, or otherwise print it before adding to the dict.
Moreover, it wouldn't be a bad idea to protect that statement with an optional binding:
if let value = newValue {
dictionary[index] = value
}
because if the value type is optional, it can be nil.
So in the end, I contacted Apple Technical Support.
They couldn't reproduce the issue.
I thought that maybe I don't need to save the currency rates, because during the quotes update, the function will check which currency rates it needs anyway, and repopulate the dictionary as needed.
So I deactivated the methods i created to save the CurrencyRateStore and to reload it again with NSKeyedUnarchiver.
Apparently, the crash is gone!