Using playground to simulate a problem with a core data based app turned up an issue I can't seem to understand.
class Pole {
var length: NSNumber!
}
var poles: [Pole] = []
let pole1 = Pole()
pole1.length = 1
poles.append(pole1)
let pole2 = Pole()
pole2.length = 2
poles.append(pole2)
var sum = poles.reduce(0) { $0 + $1.length } // error Could not find member 'length'
The property (attribute) named length is NSNumber as it is in a NSManagedObject class (entity).
Changing the Type from NSNumber! to Int! allows the line to compile and run correctly in playground.
Leaving the the Type as NSNumber! and changing the the offending line as follows:
var sum = poles.reduce(0) { $0 + Int($1.length) }
also compiles and run correctly in playground. The next step, taking this to the app, using actual NSManagedObject entity and attribute compiles but fails at runtime. The failure is 'unwrapping a nil'.
So bottom line I can't figure out how to use the reduce function when the attribute is a NSNumber and casting it to an Int doesn't seem to be acceptable.
NSNumber is a Foundation class that is used to wrap numbers that would otherwise be represented in a value type, so they can be used wherever a reference type is expected. Swift bridges integer literals to NSNumber, but not the other way around, so the original reduce closure was trying to add an integer literal to an NSNumber, getting confused, and giving you a weird error message.
Creating a new Int is one way to handle the problem:
var sum = poles.reduce(0) { $0 + Int($1.length) }
Or you can use NSNumber's integerValue property, which returns the instance's value as an Int:
var sum = poles.reduce(0) { $0 + $1.length.integerValue }
Related
When creating an instance of an object, I'm having trouble setting its properties if I assign the property to another variable.
Eg. For an object containing nested objects, I want to assign one of the children to a temporary var to make it easier to work with.
Instead of doing this (which works):
myObj.myChildObject[0].someOtherChild[0].property = "something"
I'm trying to do:
var t = myObj.myChildObject[0].someOtherChild[0]
t.property = "something"
// this doesn't throw an error but doesn't change the value of myObj
What gives?
Edit>
Here's a contrived example to illustrate:
class Car { var name: String = "" }
var merc = Car()
var obj = merc.name
merc.name = "C63 AMG"
obj = "E300"
print("merc.name: \(merc.name)") // prints merc.name: C63 AMG
print("obj.name: \(obj)") // prints obj.name: E300
var ob2 = merc
ob2.name = "Toyota"
print("ob2.name: \(ob2.name)") // prints ob2.name: Toyota
print("merc.name: \(merc.name)") // prints merc.name: Toyota
So assigning the class instance to a var creates a reference. But assigning a property of that object to another var creates a copy?
I read through https://developer.apple.com/swift/blog/?id=10 and still don't get it :(
In the above, 'obj' is not a struct, enum or tuple, so shouldn't it be a reference type?
If myObj.myChildObject[0].someOtherChild[0] is a value type (I.e. a strict, direct enum or tuple), it's being copied upon assignment to t. Subsequent mutations on t only mutate that copy, and the original instance are unchanged.
You would have to reassign t back in:
var t = myObj.myChildObject[0].someOtherChild[0]
t.property = "something"
myObj.myChildObject[0].someOtherChild[0] = t
This is discussed in the Swift language guide.
I was using this code to populate a cell in a spreadsheet using EPPlus:
using (var footerMonth1Cell = prodUsageWorksheet.Cells[columnFooterRow, MONTH1_COL])
{
footerMonth1Cell.Value = monthsTruncatedYears[0];
footerMonth1Cell.Style.Font.Size = DATA_FONT_SIZE;
footerMonth1Cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
}
It was working fine, I thought, but a user complained that it was producing "green triangles" in the cells. "monthsTruncatedYears" is a generic list of string.
So I added this helper function:
public static void SetValueIntOrStr(this ExcelRangeBase range, object value)
{
string strVal = value.ToString();
if (!String.IsNullOrEmpty(strVal))
{
double dVal;
int iVal;
if (double.TryParse(strVal, out dVal))
range.Value = dVal;
else if (Int32.TryParse(strVal, out iVal))
range.Value = iVal;
else
range.Value = strVal;
}
else
range.Value = null;
}
...which I got from here, and then tried to refactor the original code to call that helper function like so:
using (var footerMonth1Cell = prodUsageWorksheet.Cells[columnFooterRow, MONTH1_COL])
{
footerMonth1Cell.Value = SetValueIntOrStr(footerMonth1Cell, monthsTruncatedYears[0]);
footerMonth1Cell.Style.Font.Size = DATA_FONT_SIZE;
footerMonth1Cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
}
However, it won't compile, telling me, "Cannot implicitly convert type 'void' to 'object'"
Since the second arg passed to SetValueIntOrStr(), namely "value", is of type object, I assume that is the problem. So why does the compiler apparently view monthsTruncatedYears[0] as being void? In the legacy code I assigned it as the value to the cell, and it wasn't void at that time, so...?!?
I tried casting the second arg to object like so:
footerMonth1Cell.Value = ReportRunnerConstsAndUtils.SetValueIntOrStr(footerMonth1Cell, object(monthsTruncatedYears[0]));
...but that won't compute, either, with "Invalid expression term 'object'"
...and this similar attempt:
footerMonth1Cell.Value = ReportRunnerConstsAndUtils.SetValueDoubleOrIntOrStr(footerMonth1Cell, monthsTruncatedYears[0] as object);
...elicits, "Cannot implicitly convert type 'void' to 'object'"
Note that the helper method is actually misnamed in the borrowed code; instead of "SetValueIntOrStr" it should be "SetValueDoubleOrIntOrStr"
The problem is the line
footerMonth1Cell.Value = SetValueIntOrStr(footerMonth1Cell, monthsTruncatedYears[0]);
SetValueIntOrStr does not return any value (void) and therefore cannot be used to assign a value to the footerMonth1Cell.Value.
This would be valid code because the Value property is already changed inside the function:
SetValueIntOrStr(footerMonth1Cell, monthsTruncatedYears[0]);
Boxing Conversion can be acheived by using as keyword
So I tried to perform boxing using as keyword.
So I tried the below example
Eg 1:-
int i = 12;
string a = i.ToString(); // Boxing Conversion
Console.WriteLine(a); //This works
Console.ReadKey();
Eg 2:-
var aa = i as object; // This works
var a = (i as string) // Compile time error
var a = (i as string).ToString(); // Compile time error
Can anyone explain why boxing can't be performed using as keyword for a string reference type?
1)
int i = 12;
string a = i.ToString();
ToString() is not a boxing conversion at all (and I'm not sure the term is quite right - boxing is just boxing). It is conversion to string. "a" is totally different object, not related to i anymore. It's type is string and value is "12", not 12.
2)
int i = 12;
var aa = i as object;
It is boxing, but aa still keeps object of int type.
var a = (i as string)
Here you are trying to convert int to string, which is impossible to do what way.
What you are trying to do here is usual in many languages, like JavaScript. But C# has very strong rules about type conversions. And most of the time, you cannot just cast type to string and back.
There is an object called bird. I want to go to a new scene 2, 1 after that bird reaches location of bird.y = -575.
However, when debugging the code, I got
Scene 1, Layer 'actions', Frame 1, Line 44 1067: Implicit coercion of a value of type int to an unrelated type String.
How to fix it?
Code
import flash.events.MouseEvent;
import flash.display.MovieClip;
stop();
var birdVelocity:int = 0;
var stageGravity:int = 2;
function BirdFall(event:Event):void
{
bird.y = bird.y + birdVelocity;
birdVelocity = birdVelocity + stageGravity;
}
stage.addEventListener(Event.ENTER_FRAME, BirdFall);
function FlyBird(event:MouseEvent):void
{
birdVelocity = -10;
bird.gotoAndStop(2);
}
stage.addEventListener(MouseEvent.CLICK, FlyBird);
if (bird.y == 575) {
gotoAndPlay("Scene 2", 1)
}
Honestly have no idea why it's set up like this as the gotoAndPlay function on Adobe's actual page says you're supposed to put where you want to go first and the frame second, but it's swapped for some unknown reason, so you're actually supposed to write it as:
gotoAndPlay(1, "Scene 2");
I had the same problem and found out you're supposed to have the frame first and it worked for me.
Trying to implement my first Parallel::For loop with a tread local variable to sum results of the loop. My code is based on an example listed in "Visual C++ 2010, by W. Saumweber, D. Louis (German). Ch. 33, P.804).
I get stuck in the implementation with syntax errors in the Parallel::For call. The errors are as follows, from left to right: a) expected a type specifier, b) too many arguments for generic class "System::Func", c) pointer to member is not valid for a managed class, d) no operator "&" matches these operands.
In line with the book, I create a collection with data List<DataStructure^> numbers, which is subject to a calculation performed in method computeSumScore which is called by the Parallel::For routine in method sumScore. All results are summed in method finalizeSumScore using a lock.
Below I paste the full code of the .cpp part of the class, to show what I have. The data collection "numbers" may look a bit messy, but that's due to organical growth of the program and me learning as I go along.
// constructor
DataCollection::DataCollection(Form1^ f1) // takes parameter of type Form1 to give acces to variables on Form1
{
this->f1 = f1;
}
// initialize data set for parallel processing
void DataCollection::initNumbers(int cIdx)
{
DataStructure^ number;
numbers = gcnew List<DataStructure^>();
for (int i = 0; i < f1->myGenome->nGenes; i++)
{
number = gcnew DataStructure();
number->concentrationTF = f1->myOrgan->cellPtr[cIdx]->concTFA[i];
number->stringA->AddRange(f1->myGenome->cStruct[i]->gString->GetRange(0, f1->myGenome->cChars));
number->stringB->AddRange(f1->myGenome->cStruct[i]->pString);
if (f1->myGenome->cStruct[i]->inhibitFunc)
number->sign = -1;
else
number->sign = 1;
numbers->Add(number);
}
}
// parallel-for summation of scores
double DataCollection::sumScore()
{
Parallel::For<double>(0, numbers->Count, gcnew Func<double>(this, &GenomeV2::DataCollection::initSumScore),
gcnew Func<int, ParallelLoopState^, double, double>(this, &GenomeV2::DataCollection::computeSumScore),
gcnew Action<double>(this, &GenomeV2::DataCollection::finalizeSumScore));
return summation;
}
// returns start value
double DataCollection::initSumScore()
{
return 0.0;
}
// perform sequence alignment calculation
double DataCollection::computeSumScore(int k, ParallelLoopState^ status, double tempVal)
{
int nwScore;
if (numbers[k]->concentrationTF > 0)
{
nwScore = NeedlemanWunsch::computeGlobalSequenceAlignment(numbers[k]->stringA, numbers[k]->stringB);
tempVal = Mapping::getLinIntMapValue(nwScore); // mapped value (0-1)
tempVal = (double) numbers[k]->sign * tempVal * numbers[k]->concentrationTF;
}
else
tempVal = 0.0;
return tempVal;
}
// locked addition
void DataCollection::finalizeSumScore(double tempVal)
{
Object^ myLock = gcnew Object();
try
{
Monitor::Enter(myLock);
summation += tempVal;
}
finally
{
Monitor::Exit(myLock);
}
}
Once this problem is solved I need to ensure that the functions called (computeGlobalSequenceAlignment and getLinIntMapvalue) are thread safe and the program doesn't get stalled on multiple treads accessing the same (static) variables. But this needs to work first.
Hope you can help me out.
Hans Passant answered my question in the comments (include full method name, add comma). Yet I cannot mark my question as answered, so this answer is to close the question.