Using Block with objectAtIndex method of NSArray - iphone

Here is the code:
TrailLayer * layer = (TrailLayer*)[_layers objectAtIndex:(int)^{
if (_segmentNumber < [_segmentArray count]) {
return _segmentNumber;
} else {
return _segmentNumber - 1;
}
}];
what is the problem here? Here, all the _Variables are IVARs. I am first time using Blocks, can someone help me identifying this problem. Its giving EXC_BAD_INSTRUCTION here.
Thanks.

Try this code, First write block to get the index number, and use that number to retrieve value from array
int (^segmentIndex)(int) = ^(int segmentNumber){
if (segmentNumber < [_layers count]) {
return segmentNumber;
} else {
return segmentNumber - 1;
}
};
NSLog(#"Trail Layer %#", [_layers objectAtIndex:segmentIndex(_segmentNumber)]);
TrailLayer * layer = (TrailLayer*)[_layers objectAtIndex:segmentIndex(_segmentNumber)];

A block is a runnable thing like a function, basically a piece of code that you can store, pass around, and run later (or not). You want to pass an integer to objectAtIndex:, so passing a block makes absolutely no sense whatsoever.
What you could have meant to do was run the block, and then pass its result to objectAtIndex:. In that case, you would have to run the block:
TrailLayer * layer = [_layers objectAtIndex:^{
if (_segmentNumber < [_segmentArray count]) {
return _segmentNumber;
} else {
return _segmentNumber - 1;
}
}()]; // <-- run the block
However, defining a block (which is a full-fledged object, and does a lot of fancy stuff) just to run it immediately is kinda silly. If you want to just be able to use a piece of code as an expression, you could use the "statement expressions" language extension supported by both GCC and LLVM:
TrailLayer * layer = [_layers objectAtIndex:({
int result;
if (_segmentNumber < [_segmentArray count]) {
result = _segmentNumber;
} else {
result = _segmentNumber - 1;
}
result;
})];
But really you should just do a regular C conditional expression in this case:
TrailLayer * layer = [_layers objectAtIndex:
_segmentNumber < [_segmentArray count] ?
_segmentNumber : _segmentNumber - 1];

Related

Why can't I create a pointer to a TTreeNode in a TTreeView

Using C++ Builder in Rad Studio 10.4
Why can't I create a pointer to a TTreeNode?
I have a TTreeView called BookmarksTree, and I want to loop through all of its nodes. When I try and compile this:
TTreeNode *Node;
Node = BookmarksTree->Items[1];
I get a compiler error:
assigning to 'Vcl::Comctrls::TTreeNode *' from incompatible type 'Vcl::Comctrls::TTreeNodes'
According to Vcl.ComCtrls.TCustomTreeView.Items, I should be able to use
MyTreeNode = TreeView1->Items[[1]];
Has anyone any idea what's wrong here?
BookmarksTree->Items is a pointer to a single TTreeNodes object. You are trying to perform pointer arithmetic to access a node as if the Items were an array of TTreeNode* pointers, which is simply not the case.
You need to use the TTreeNodes::Item[] sub-property instead, eg:
int count = BookmarksTree->Items->Count;
for(int i = 0; i < count; ++i)
{
TTreeNode *Node = BookmarksTree->Items->Item[i];
...
}
Alternatively, you can use the TTreeNodes::operator[], but that requires you to dereference the TTreeNodes* pointer first, eg:
int count = BookmarksTree->Items->Count;
for(int i = 0; i < count; ++i)
{
TTreeNode *Node = (*(BookmarksTree->Items))[i];
...
}
Alternatively, in the Clang-based compilers only, you can use C++ iterators, per C++ Iterator Support for Delphi Enumerable Types and Containers, eg:
auto iter = std::begin(BookmarksTree->Items);
auto end = std::end(BookmarksTree->Items);
while (iter != end)
{
TTreeNode *Node = *iter++;
...
}
Or a range-for loop (which uses iterators internally):
for(TTreeNode *Node : BookmarksTree->Items)
{
...
}

Why does my code not work? (Repeating loop with return)

I have no idea why this code does not work. The whole idea about it is to make the value bigger until it's bigger than score.
if(score > height && rocketlaunch == false)
{
#try
{
height = [self makebigger:height];
}
#catch (NSException *exception)
{
height = height + 4000;
}
upgradeRocket.center = CGPointMake((rand()%200), -50);
rocketlaunch = true;
}
-(int)makebigger:(int)heightnr {
heightnr = heightnr + (1000 * rand() %5);
if(score > heightnr) {
[self makebigger:heightnr];
return heightnr;
} else {
return heightnr;
}
}
Does anyone know how to fix this? Or have a alternative way?
P.S.
The error displayed was:
Implicit conversion of int to id is disallowed with ARC
and
Incompatible integer to pointer conversion returning int from a function with result type id
Thank you in advance.
EDIT:
It works this way thank you very much :)
EDIT:
I got a new problem hard to solve:
this gives the error > tread 1 exc bad acces code = 2
change -makebigger:(int)heightnr to - (int)makebigger:(int)heightnr.
You have to specify the return type.
And you have to return something if the condition is true, too.
Until now I didn't even know it was possible to use no return type. But apparently it is.
Assuming score is an instance variable of type int then you have two errors in your code: first you must specify the return type of the method otherwise id is assumed; and second every path must return a value and your recursive call does not do this. Correcting those gives:
- (int)makebigger:(int)heightnr
{
heightnr = heightnr + (1000 * rand() %5);
if(score > heightnr)
return [self makebigger:heightnr];
else
return heightnr;
}
While this should work for simple value-based algorithms like this it is more usual to use iteration rather than recursion, as in:
- (int)makebigger:(int)heightnr
{
while (score > heightnr)
heightnr = heightnr + (1000 * rand() %5);
return heightnr;
}
I am going to take a guess that your "score" variable is an NSNumber. The compiler is complaining because you are trying to compare an NSNumber (object) to an int.
Try this:
if([score intValue] > heightnr)

IsEqual Method Sent to Deallocated Instance When Trying to Add Object To MutableOrderedSet

I have an NSMutableOrdered set that holds two types of objects (that I created), Pieces and Others.
Piece and Other both have their isEqual method overridden like so:
Piece:
- (BOOL)isEqual:(Piece *)object
{
if (([title isEqualToString:[object title]])
&& ([composer isEqualToString:[object composer]])
&& (major == [object major])
&& (tempo == [object tempo])
&& (pieceKey == [object pieceKey])
&& (pieceTime == [object pieceTime]))
return YES;
else
return NO;
}
Other:
- (BOOL)isEqual:(Other *)object
{
if (([title isEqualToString:[object title]])
&& ([subTitle isEqualToString:[object subTitle]])
&& ([description isEqualToString:[object description]])
&& (otherTime == [object otherTime]))
return YES;
else
return NO;
}
I also override the hash of both classes to create a unique hash for each instance (by getting the int value of the ivars and adding them).
In my app, An Other is removed from the set, then when I try to add a piece to the set, I get this:
-[Other isEqual:]: message sent to deallocated instance 0x80d5680
here is the hash method:
- (NSUInteger)hash
{
NSUInteger prime = 31;
NSUInteger result = 1;
result = prime * (result + [title intValue]);
result = prime * (result + [composer intValue]);
result = prime * (result + major);
result = prime * (result + tempo);
result = prime * (result + pieceKey);
result = prime * (result + pieceTime);
return result;
}
If anybody knows why this is happening, I would really some help.
Thanks,
This is not really an answer, but it will help us to find the problem. Some questions:
Are you sure that 0x80d5680 is the instance that was previously removed?
How do you remove it from the set?
Do you modify the state of your objects after adding them?
Are you sure that your hashes are unique (sum of int value of ivars sound sounds suspicious).
Finally, make sure that your objects obey this rule:
If two objects are equal, they must have the same hash value. This last point is particularly important if you define isEqual: in a subclass and intend to put instances of that subclass into a collection. Make sure you also define hash in your subclass.
See NSObject Protocol Reference for more information.
I believe isEqual is called on members as part of the set comparison, as a set is a group of unique items. Perhaps you should add the member to the new set before removing from the old one?
Hope this helps!

While loop not stopping with given criteria

Im having a bit of a problem with this while loop. I keep getting a crash because it continues to execute the loop even when it has found the object and supposedly increased the int.
Any ideas I have plenty of other loops set up in a similar way and they all work fine.
int possible = 0;
while (possible < [possibleAthetes count]) {
if ([[[possibleAthetes objectAtIndex:possible]valueForKey:#"ID"]intValue] == [self.athleteID intValue]) {
[possibleAthetes removeObjectAtIndex:possible];
possible = [possibleAthetes count] ;
}
possible ++;
}
You are incrementing possible even when you remove an object. Break out of the loop.
If you need to get rid of multiple athletes, leave out the break(No need to increment because of the removed object).
int possible = 0;
while (possible < [possibleAthetes count]) {
BOOL criteriaMatch = ([[[possibleAthetes objectAtIndex:possible]valueForKey:#"ID"]intValue] == [self.athleteID intValue]);
if (criteriaMatch){
[possibleAthetes removeObjectAtIndex:possible];
break;
} else {
possible++;
}
}

Counter in while within while not working

I'm having a strange problem with the following code:
int c = [whatsNewArray1 count];
int t = [dames count];
int i = 0;
int o= 0;
NSMutableArray *finalWhatsNew = [[NSMutableArray alloc]init];
while (i<c){
NSLog(#"teller array %i", i);
while(t>o){
NSLog(#"dames %i", i);
if ([[[dames objectAtIndex:o] productId] isEqualToString:[whatsNewArray1 objectAtIndex:i]]){
[finalWhatsNew addObject:[dames objectAtIndex:o]];
NSLog(#"inner dames%i", i);
}
o++;
}
i++;
}
This code retrieves al the entries from the "dames" array which are stated in the "finalWhatsNew" array. Problem with this is that the if is only get called the first time.
To make it a little bit clearer, the whole code is working fine, but as soon "i" is ++ to 1. the if statement isn't called. It looks like ios i canceling it out after the first time for performance reason or somethins like that. Anybody has any ideas?
Thnx!!!
After inner loop is finished for the first time the o counter is equal to array's count and so it won't enter the loop again. To make it work you must reset o counter on each iteration of the outer loop:
while (i<c){
o = 0;
while (t > o)
...
Edit: For clearer code (and probably better performance) you can use fast enumeration instead of usual for/while loops:
for (NSString *searchId in whatsNewArray1){
for (YourObject *obj in dames){
if ([[obj productId] isEqualToString:searchId])
[finalWhatsNew addObject: obj];
}
}
Edit2: Also you can eliminate 2nd loop by using NSPredicate to filter your array:
for (NSString *searchId in whatsNewArray1){
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"productId == %#",searchId];
[finalWhatsNew addObjectsFromArray:[dames filteredArrayUsingPredicate:predicate]];
}
As Vladimir says, you need to reset o after each iteration of the outer loop. Ideally you switch to for-statements here, as they fit what you're doing exactly:
for (int i=0; i<c; ++i) {
// ...
for (int o=0; o<t; ++o) {
// ...