Unable to assign class objects to dictionary member of another class object in Swift 3 - ios

I have a class ChessSquare with its own attributes and methods. I have another class ChessBoard, which has an attribute of dictionary type.
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
.....
One of the methods of the ChessBoard class builds the chessSquare object and assigns it to chessBoard object as below.
self.squares[squareName] = chessSquare
Before Swift3 this code was working fine. After upgrading to Swift3 the assignment stopped working.
Using breakpoints I see that chessSquare objects are built as expected. squareName variable has the expected value. The "self", which is the chessBoard object has proper initial values. But the dictionary assignment gets skipped without any error in the above mentioned code.
Is this anything related to Swift3. I browsed and could not get specific solution to my case.
Adding more code for clarity.
class ChessSquare {
let squareColor: UIColor
let squareShade: Int
let squareName: Int
let squareSize: CGSize
let minXY: CGPoint
let maxXY: CGPoint
let squareOrigin: CGPoint
var hasPiece = false
weak var chessPiece: ChessPiece?
let squareSprite: SKSpriteNode
let squareType: SquareType
//more data members
init(squareColor: UIColor, squareShade: Int, squareName: Int, squareSize: CGSize, minXY: CGPoint, maxXY: CGPoint, squareOrigin: CGPoint, squareSprite: SKSpriteNode, squareType: SquareType) {
self.squareColor = squareColor
self.squareShade = squareShade
self.squareName = squareName
self.squareSize = squareSize
self.minXY = minXY
self.maxXY = maxXY
self.squareOrigin = squareOrigin
self.squareSprite = squareSprite
self.squareType = squareType
}
//more methods
}
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
var byteBoard: [UInt8] = [UInt8](repeating: UInt8(0), count: 66)
var byteBoardHashVal: Int = 0
// [ byteBoardHashVal : ((byteBoard, evalnVal), repCnt) ]
var byteBoards: [Int : (([UInt8],Double?), Int)] = [:]
var TT: [Int : Double] = [:]
var TTReuseCount = 0
var whitePawnlessFileByte: UInt8 = 255
var blackPawnlessFileByte: UInt8 = 255
var maxXY: CGPoint = CGPoint()
var minXY: CGPoint = CGPoint()
var oldTouchedSquare: ChessSquare?
init() {
squares = [:]
byteBoard = [UInt8](repeating: UInt8(0), count: 66)
byteBoardHashVal = 0
byteBoards = [:]
TT = [:]
TTReuseCount = 0
whitePawnlessFileByte = 255
blackPawnlessFileByte = 255
}
func drawFlippedChessBoard(_ view: SKView, scene: GameScene) {
....
....
for row in 0..<8 {
for col in 0..<8 {
let squareName = row * 8 + col
....
let chessSquare = ChessSquare(squareColor: currentColor, squareShade: squareShade, squareName: squareName, squareSize: squareSize, minXY: minXY, maxXY: maxXY, squareOrigin: squareOrigin, squareSprite: squareSprite, squareType: squareType)
....
self.squares[squareName] = chessSquare
....
}
....
}
....
}
//more methods
}

I have a difficulty in understanding if squareName is a string or a integer. It sounds like a string, I would suggest to name it chessSquareIndex or squareIndex if in case it is Int and you want to index chess square.
Based on your comment above I did a small test to verify that it works correctly if squareName is Int and here is the result,
class ChessSquare { }
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
func getChessSquare(at squareIndex: Int) {
let chessSquare = ChessSquare()
return squares[squareIndex] = chessSquare
}
}
And this seems to work fine with Swift 3. Also notice that it is not required to call self, unless you are inside escaping closures.

Related

Getting EXC_BAD_ACCESS while the object exists

I'm developing a music sequencer with a standard piano roll type UI.
It was working well until I made some changes in the model side but it suddenly started to report EXC_BAD_ACCESS at (seemingly) unrelated part.
What's strange is all the necessary variables have their values properly and actually I can print values with po.
In my understanding, EXC_BAD_ACCESS happens when an object doesn't exist, so this seems quite strange.
My question is:
Is it common to EXC_BAD_ACCESS even the values are there?
If that's the case what is the possible situation to cause that?
Any suggestion is helpful. Thanks
[Below are the codes]
In my subclass of UICollectionViewLayout:
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// note cells
let cv = self.collectionView as! YMPianoRollCollectionView;
let pianoRoll = cv.pianoRollViewController;
// Call the below func to get the indexes of the Note Objects included in the specified rect
let indexArray: Array<Int> = pianoRoll!.getNoteIndexes(inRect:rect, useOnlyStartTime: false);
var retArray : [UICollectionViewLayoutAttributes] = []
for i in indexArray {
if let _ = pianoRoll?.pattern.eventSequence[i] as? YMPatternEventNoteOn {
retArray.append( self.layoutAttributesForPatternEventInfo(i) )
}
}
return retArray
}
In my "piano roll" class which contains UICollectionView
func getNoteIndexes(inRect rect:CGRect, useOnlyStartTime: Bool) -> Array<Int> {
//
// Transform given values into musical values
//
let musicRange :YMMusicalValueRange = screenInfo.getMusicalRange(rect);
let startTime = musicRange.origin.time;
let endTime = musicRange.origin.time + musicRange.size.timeLength;
let lowNoteU = musicRange.origin.noteNumber;
let highNoteU = musicRange.origin.noteNumber + musicRange.size.numberOfNotes;
var retArray : [Int] = []
for i in 0..<pattern.eventSequence.count {
if let e = pattern.eventSequence[i] as? YMPatternEventNoteOn {
//
// Prepare ranges
//
let noteNo = e.noteNo; //<- App Crashes Here with BAD_ACCESS
let noteStTime = e.time;
let noteEnTime = e.time + e.duration;
let targetNoteRange = Range<Int>(uncheckedBounds: (lowNoteU, highNoteU));
let targetTimeRange = Range<Int64>(uncheckedBounds: (startTime, endTime))
let noteTimeRange = Range<Int64>(uncheckedBounds: (noteStTime, noteEnTime))
//
// Check the match
//
let noteMatches = targetNoteRange.contains(noteNo);
let timeMatches = useOnlyStartTime ? targetTimeRange.contains(noteStTime)
: targetTimeRange.overlaps(noteTimeRange)
if noteMatches && timeMatches {
retArray.append( i );
NSLog("XXX Found: note \(noteNo) at \(e.time)");
}
}
}
return retArray;
}
Error:- The object states when it crashed
EDIT
Here's the YMPatternEventNoteOn declaration
class YMPatternEvent : Codable, CustomDebugStringConvertible {
var time : YMSequenceTime = 0
// Some utility funcs follow
// ...
}
class YMPatternEventNoteOn : YMPatternEvent {
var noteNo : Int = 64
var velocity : Int = 127
var duration : YMSequenceTime = 480
var tempBendId : Int = 0;
var tempVibratoId : Int = 0;
var tempArpeggioId : Int = 0;
convenience init(time :YMSequenceTime, noteNo : Int, velocity: Int, duration: YMSequenceTime) {
self.init();
self.time = time;
self.noteNo = noteNo;
self.velocity = velocity;
self.duration = duration;
}
// Other methods follow
// ...
}
EDIT2
Note event is created by the user's action
//
// In YMPattern object
//
func insertNote(time:YMSequenceTime, noteNo:Int, velocity:Int, duration:YMSequenceTime) -> Int
{
let onEvent = YMPatternEventNoteOn(time: time, noteNo: noteNo, velocity: velocity, duration: duration);
let retIndex = insertEvent(onEvent);
return retIndex;
}
func insertEvent(_ event: YMPatternEvent) -> Int {
let atTime = event.time;
var retIndex : Int = 0;
if(eventSequence.count<1){
// If it's the first event just add it
eventSequence.append(event);
retIndex = 0;
} else {
// If any event already exists, insert with time order in consideration
var i : Int = 0;
while( atTime > eventSequence[i].time ){
i += 1;
if( i >= eventSequence.count ){
break;
}
}
retIndex = i;
eventSequence.insert(event, at: i)
}
}
//
// In pianoroll view controller
//
func actionButtonReleased(afterDragging: Bool) {
let values:YMMusicalValuePoint = screenInfo.getMusicalPosition(cursorPosition);
// insert new event with default velocity and duration
let _ = pattern.insertNote(time: values.time, noteNo: values.noteNumber, velocity: 127, duration: screenInfo.timeTicsPerDivision());
collectionView.reloadData();
}
I've solved it in a way.
By setting the optimization level to "Whole Module Optimization" it stopped reporting the error.
I don't know what is happening internally but if someone is having the same issue, this might work as a quick fix.

Local variable won't copy to global variable (swift)

I need to copy a local variable tuple (sortedMarkers) from the function closestMarker() into the global variable sortedMarkerGlobal, however it doesn't seem to be working thus far. I want to use sortedMarkersGlobal in another function but it's count is zero (empty).
class MapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
var sortedMarkerGlobal: [(lat: String, long: String)] = []
var nextParkCount: Int = 1 // Used to iterate over sortedMarkers with nextPark IBAction button
let locationManager = CLLocationManager()
var lastLocation: CLLocation? = nil // Create internal value to store location from didUpdateLocation to use in func showDirection()
var isAnimating: Bool = false
var dropDownViewIsDisplayed: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
....
}
func closestMarker(userLat: Double, userLong: Double)-> [(lat: String, long: String)] {
/*
var lengthRow = firstRow.count
while (sortedMarkers.count != lengthRow) { // There are markers that haven't been added to sortedMarkers
// Haversine formula
// add nearest marker to sortedMarkers
// Remove recently added marker from ParseViewControler() type tuple
// Recurse until all markers are in sortedMarkers
}
*/
let markerToTest = ParseViewController()
let firstRow = markerToTest.returnParse()
let lat2 = degreesToRadians(userLat) // Nearest lat in radians
let long2 = degreesToRadians(userLong) // Nearest long in radians
let R = (6371).doubleValue // Radius of earth in km
var closestLat: Double? = nil // Used to store the latitude of the closest marker
var closestLong: Double? = nil // Used to store the longitude of the closest marker
//
var indexToDelete: Int = 0 // Used to delete the marker which has been added to sortedMarkers tuple
let lengthRow = firstRow.count
var latLongs: (String, String)
var sortedMarkers: [(lat: String, long: String)] = []
var dynamicRow = firstRow // Tuple that has markers deleted from it
while (sortedMarkers.count != lengthRow) { // Store markers from closest to furtherst distance from user
var shortestDistance = 100.00E100
for (var i = 0; i < dynamicRow.count; i++) {
let testMarker = dynamicRow[i]
let testLat = (testMarker.lat as NSString)
let testLong = (testMarker.long as NSString)
let doubleLat = Double(testLat as String)
let doubleLong = Double(testLong as String)
let lat1 = degreesToRadians(doubleLat!)
let long1 = degreesToRadians(doubleLong!)
let dLat = lat2 - lat1
let dLong = long2 - long1
let a = ((sin((dLat)/2)) * (sin((dLat)/2))) + (cos(lat1)*cos(lat2)*((sin((dLong)/2)) * (sin((dLong)/2))))
let b = sqrt(a)
let d = (2*R) * (asin(b)) // Haversine formula
if (d < shortestDistance) {
closestLat = (doubleLat)
closestLong = (doubleLong)
shortestDistance = d
indexToDelete = i// Keep updating index of closest marker to later be removed
}
}
latLongs = (String(closestLat!), String(closestLong!)) // Each time for loop will find closest marker ~ NOT WORKING ~
sortedMarkers.append(latLongs)
dynamicRow.removeAtIndex(indexToDelete) // Remove marker that has just been added
}
sortedMarkerGlobal = sortedMarkers
return sortedMarkers
}
sortedMarkerGlobal is not a global variable. currently it is an instance variable like any of the other variables you have defined (locationManager, lastLocation, isAnimating, etc).
if you want it to be a global variable you should define it outside of your class MapViewController.
another option would be to make it a class variable of MapViewController by defining it as:
class MapViewController: UIViewController {
static var sortedMarkerGlobal: [(lat: String, long: String)] = []
}
Well I tried to test your code in playground and it worked for me
import UIKit
class Test {
var global:[(lat: String, long: String)] = []
func test1() {
var sortedMarkers: [(lat: String, long: String)] = []
var latLongs: (String, String)
latLongs = ("123", "123")
sortedMarkers.append(latLongs)
global = sortedMarkers
}
}
let test:Test = Test()
test.test1();
print("global \(test.global)")
And the result is global [("123", "123")] so I guess it should also work for you, the problem might be somewhere else.
I would suggest that you just use a closures instead of saving to a global variable. This lets you pass in the parameters and return your result based on that. You can save that result in the function that called it and do what ever you want from there.

Local variable won't copy to global variable

I need to copy a local variable tuple (sortedMarkers) from the function closestMarker() into the global variable sortedMarkerGlobal, however it doesn't seem to be working thus far. I want to use sortedMarkersGlobal in another function but it's count is zero (empty).
class MapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
var sortedMarkerGlobal: [(lat: String, long: String)] = []
var nextParkCount: Int = 1 // Used to iterate over sortedMarkers with nextPark IBAction button
let locationManager = CLLocationManager()
var lastLocation: CLLocation? = nil // Create internal value to store location from didUpdateLocation to use in func showDirection()
var isAnimating: Bool = false
var dropDownViewIsDisplayed: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
....
}
func closestMarker(userLat: Double, userLong: Double)-> [(lat: String, long: String)] {
/*
var lengthRow = firstRow.count
while (sortedMarkers.count != lengthRow) { // There are markers that haven't been added to sortedMarkers
// Haversine formula
// add nearest marker to sortedMarkers
// Remove recently added marker from ParseViewControler() type tuple
// Recurse until all markers are in sortedMarkers
}
*/
let markerToTest = ParseViewController()
let firstRow = markerToTest.returnParse()
let lat2 = degreesToRadians(userLat) // Nearest lat in radians
let long2 = degreesToRadians(userLong) // Nearest long in radians
let R = (6371).doubleValue // Radius of earth in km
var closestLat: Double? = nil // Used to store the latitude of the closest marker
var closestLong: Double? = nil // Used to store the longitude of the closest marker
//
var indexToDelete: Int = 0 // Used to delete the marker which has been added to sortedMarkers tuple
let lengthRow = firstRow.count
var latLongs: (String, String)
var sortedMarkers: [(lat: String, long: String)] = []
var dynamicRow = firstRow // Tuple that has markers deleted from it
while (sortedMarkers.count != lengthRow) { // Store markers from closest to furtherst distance from user
var shortestDistance = 100.00E100
for (var i = 0; i < dynamicRow.count; i++) {
let testMarker = dynamicRow[i]
let testLat = (testMarker.lat as NSString)
let testLong = (testMarker.long as NSString)
let doubleLat = Double(testLat as String)
let doubleLong = Double(testLong as String)
let lat1 = degreesToRadians(doubleLat!)
let long1 = degreesToRadians(doubleLong!)
let dLat = lat2 - lat1
let dLong = long2 - long1
let a = ((sin((dLat)/2)) * (sin((dLat)/2))) + (cos(lat1)*cos(lat2)*((sin((dLong)/2)) * (sin((dLong)/2))))
let b = sqrt(a)
let d = (2*R) * (asin(b)) // Haversine formula
if (d < shortestDistance) {
closestLat = (doubleLat)
closestLong = (doubleLong)
shortestDistance = d
indexToDelete = i// Keep updating index of closest marker to later be removed
}
}
latLongs = (String(closestLat!), String(closestLong!)) // Each time for loop will find closest marker ~ NOT WORKING ~
sortedMarkers.append(latLongs)
dynamicRow.removeAtIndex(indexToDelete) // Remove marker that has just been added
}
sortedMarkerGlobal = sortedMarkers
return sortedMarkers
}

Get all the keys for a class

Recently I work with the function func setValue(_ value: AnyObject?, forKey key: String) of the NSKeyValueCoding protocol to change the text color of a UIPickerDate in the following way:
class ColoredDatePicker: UIDatePicker {
var changed = false
override func addSubview(view: UIView) {
if !changed {
changed = true
self.setValue(UIColor(red: 0.42, green: 0.42, blue: 0.42, alpha: 1), forKey: "textColor")
}
super.addSubview(view)
}
}
Regarding the answer in this question. It works perfectly.
But my answer here is:
How do I know what names that the class provide like the textColor used above?.
I'm trying to find anything get all the names or in the documentation, but so far I don't find anything yet to get the keys provided by a class like in the above case.
The objective-c runtime provides this type of reflection for properties:
id UIDatePickerClass = objc_getClass("UIDatePicker");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(UIDatePickerClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
reference doc: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html#//apple_ref/c/func/class_copyPropertyList
edit - swift has basic reflection too: https://stackoverflow.com/a/24069875/1385467
For those we want a Swift solution like the #Adam, here is :
var propertiesCount : CUnsignedInt = 0
let propertiesInAClass = class_copyPropertyList(UIDatePicker.self, &propertiesCount)
var propertiesDictionary : NSMutableDictionary = NSMutableDictionary()
for var i = 0; i < Int(propertiesCount); i++ {
var property = propertiesInAClass[i]
var propName = NSString(CString: property_getName(property), encoding: NSUTF8StringEncoding)
println(propName)
}
Swift 4.1:
var propertiesCount: CUnsignedInt = 0
let propertiesInAClass = class_copyPropertyList(UIDatePicker.self, &propertiesCount)!
var propertiesDictionary: NSMutableDictionary = [:]
for i in 0..<propertiesCount {
var property = propertiesInAClass[Int(i)]
let propName = NSString(cString: property_getName(property), encoding: String.Encoding.utf8.rawValue)
print(propName)
}
Some changes to #Victor Sigler & #dwlz, to get current values for all keys:
var propertiesCount : CUnsignedInt = 0
let propertiesInClass = class_copyPropertyList(UIDatePicker.self, &propertiesCount)
print("Prperties:\(propertiesCount)")
for i in 0..<propertiesCount
{
let property = propertiesInClass![Int(i)]
let name = NSString(cString: property_getName(property), encoding: String.Encoding.utf8.rawValue)
print("\(i + 1). Key:\(name!),value \(String(describing: self.value(forKey: name! as String)))")
}

Swift dictionary change the variable of the value

I'm sorry if it's already discussed, but i don't find the solution
I have a struct:
struct Point2D
{
var x: Double = 0.0
var y: Double = 0.0
}
And I have a class also:
class Letter
{
var points3D: Dictionary<String,Point3D> = [:]
var points2D: Dictionary<String,Point2D> = [:]
var edges: Dictionary<String,Edge> = [:]
}
In some view:
class GraphicView: UIView {
var letter:Letter!
override func drawRect(rect: CGRect) {
for (name,point3d) in letter.points3D {
//Says String isn't convertable to DictionaryIndex<String,Point2D>
letter.points2D[name]!.x = point3d.x * (perspective / (perspective - point3d.z)) + imageWidth/2
letter.points2D[name]!.y = point3d.y * (perspective / (perspective - point3d.z)) + imageHeight/2
}
}
I'm trying to find some value for key (I know it exists) and change variables of this value to another. What's wrong?
Maybe perspective, imageWidth or imageHeight is not Double?
try:
class GraphicView: UIView {
var letter:Letter!
override func drawRect(rect: CGRect) {
for (name,point3d) in letter.points3D {
letter.points2D[name]!.x = point3d.x * (Double(perspective) / (Double(perspective) - point3d.z)) + Double(imageWidth)/2
letter.points2D[name]!.y = point3d.y * (Double(perspective) / (Double(perspective) - point3d.z)) + Double(imageHeight)/2
}
}

Resources