interacting with UIViews stored inside a NSMutableArray - objective-c

a big noob needs help understanding things.
I have three UIViews stored inside a NSMutableArray
lanes = [[NSMutableArray arrayWithCapacity:3] retain];
- (void)registerLane:(Lane*)lane {
NSLog (#"registering lane:%i",lane);
[lanes addObject:lane];
}
in the NSLog I see: registering lane:89183264
The value displayed in the NSLog (89183264) is what I am after.
I'd like to be able to save that number in a variable to be able to reuse it elsewhere in the code.
The closest I could come up with was this:
NSString *lane0 = [lanes objectAtIndex:0];
NSString *description0 = [lane0 description];
NSLog (#"description0:%#",description0);
The problem is that description0 gets the whole UIView object, not just the single number (dec 89183264 is hex 0x550d420)
description0's content:
description0:<Lane: 0x550d420; frame = (127 0; 66 460); alpha = 0.5; opaque = NO; autoresize = RM+BM; tag = 2; layer = <CALayer: 0x550d350>>
what I don't get is why I get the correct decimal value with with NSLog so easily, but seem to be unable to get it out of the NSMutableArray any other way. I am sure I am missing some "basic knowledge" here, and I would appreciate if someone could take the time and explain what's going on here so I can finally move on. it's been a long day studying.
why can't I save the 89183264 number easily with something like:
NSInteger * mylane = lane.id;
or
NSInteger * mylane = lane;
thank you all

I'm really confused as to why you want to save the memory location of the view? Because that's what your '89183264' number is. It's the location of the pointer. When you are calling:
NSLog (#"registering lane:%i",lane);
...do you get what's actually being printed out there? What the number that's being printed means?
It seems like a really bad idea, especially when if you're subclassing UIView you've already got a lovely .tag property which you can assign an int of your choosing.
You're making life infinitely more complex than it needs to be. Just use a pointer. Say I have an array containing lots of UIViews:
UIView *viewToCompare = [myArray objectAtIndex:3];
for (id object in myArray) {
if (object == viewToCompare) {
NSLog(#"Found it!");
}
}
That does what you're trying to do - it compares two pointers - and doesn't need any faffing around with ints, etc.

Related

Converting a string variable from Binary to Decimal in Objective C

Im trying to create a Binary to Decimal calculator and I am having trouble doing any sort of conversion that will actually work. First off Id like to introduce myself as a complete novice to objective c and to programming in general. As a result many concepts will appear difficult to me, so I am mostly looking for the easiest way to understand and not the most efficient way of doing this.
I have at the moment a calculator that will accept input and display this in a label. This part is working fine and I have no issues with it. The variable that the input is stored on is _display = [[NSMutableString stringWithCapacity:20] retain];
this is working perfectly and I am able to modify the data accordingly. What I would like to do is to be able to display an NSString of the conversion in another label. At the moment I have tried a few solutions and have not had any decent results, this is the latest attempt
- (NSMutableString *)displayValue2:(long long)element
{
_str= [[NSMutableString alloc] initWithString:#""];
if(element > 0){
for(NSInteger numberCopy = element; numberCopy > 0; numberCopy >>= 1)
{
[_str insertString:((numberCopy & 1) ? #"1" : #"0") atIndex:0];
}
}
else if(element == 0)
{
[_str insertString:#"0" atIndex:0];
}
else
{
element = element * (-1);
_str = [self displayValue2:element];
[_str insertString:#"0" atIndex:0];
NSLog(#"Prima for: %#",_str);
for(int i=0; i<[_str length];i++)
_str = _display;
NSLog(#"Dopo for: %#",_str);
}
return _str;
}
Within my View Controller I have a convert button setup, when this is pressed I want to set the second display field to the decimal equivalent. This is working as if I set displayValue2 to return a string of my choosing it works. All I need is help getting this conversion to work. At the moment this bit of code has led to "incomplete implementation" being displayed at the to of my class. Please help, and cheers to those who take time out to help.
So basically all you are really looking for is a way to convert binary numbers into decimal numbers, correct? Another way to think of this problem is changing a number's base from base 2 to base 10. I have used functions like this before in my projects:
+ (NSNumber *)convertBinaryStringToDecimalNumber:(NSString *)binaryString {
NSUInteger totalValue = 0;
for (int i = 0; i < binaryString.length; i++) {
totalValue += (int)([binaryString characterAtIndex:(binaryString.length - 1 - i)] - 48) * pow(2, i);
}
return #(totalValue);
}
Obviously this is accessing the binary as a string representation. This works well since you can easily access each value over a number which is more difficult. You could also easily change the return type from an NSNumber to some string literal. This also works for your element == 0 scenario.
// original number wrapped as a string
NSString *stringValue = [NSString stringWithFormat:#"%d", 11001];
// convert the value and get an NSNumber back
NSNumber *result = [self.class convertBinaryStringToDecinalNumber:stringValue];
// prints 25
NSLog(#"%#", result);
If I misunderstood something please clarify, if you do not understand the code let me know. Also, this may not be the most efficient but it is simple and clean.
I also strongly agree with Hot Licks comment. If you are truly interested in learning well and want to be an developed programmer there are a few basics you should be learning first (I learned with Java and am glad that I did).

Recursivity in methods, Algorithm and NSValue issue

-(BOOL)isInArray:(CGPoint)point{
if ([valid count]==0) {
return NO;
}
for (NSValue *value in valid) {
CGPoint er=[value CGPointValue];
if( CGPointEqualToPoint(point,er)) return NO;
}
return YES;
}
-(void)check:(CGPoint)next{
if (!next.y==0) {
int ics=(int) next.x;
int igrec=(int)next.y;
if (mat[ics][igrec]==mat[ics-1][igrec]){
if (![self isInArray:next]) {
[valid addObject:[NSValue valueWithCGPoint:next]];
NSLog(#"valid y!=0 : %#",valid);
[self check:CGPointMake(ics-1, igrec)];
}
}
}
}
y are columns , x are rows , mat is a C matrix
what i'm trying to do here is this:i get a point, next, in a matrix,mat (i'll use struct but for the scope of testing i use CGPoint..it's basicaly the same thing), and for that point i check if it's on the first row and if it's not i check if the value is equal to the value of the row above .If it is, i add the coord of the point into an array and move to the value above (recursively). I have ifs for left,right, and below too...but the idea is the same.
My issues:
For some reason it doesn't work as it should, even with a mat full of 1 values
The NSMutableArray i use to store the points is always null (note that the NSLog gets called so it should've added an object already)
Does recursivity work with methods?
If you have a better idea how to do this...i'm listening
The "valid" array is nil because you haven't allocated it. (You can send an addObject: message, or any message, to a nil pointer--it just doesn't do anything.) Make sure you've got
valid = [[NSMutableArray alloc] init];
somewhere before you're calling this code.
Also, "!next.y==0" is questionable. It might turn out to be identical to "next.y != 0" even if ! has a higher precedence that ==, but I wouldn't guarantee it. That's all I spot for now, without really grokking what this code is trying to do..
Oh, another quick note: Instead of writing your own isInArray, just use NSArray's containsObject:. The inner part of the check method (second indent) is then
NSValue* pointVal = [NSValue valueWithCGPoint:next];
if ( ![valid containsObject:pointVal] )
{
[valid addObject:next];
[self check:CGPointMake(ics-1, igrec)];
}
Or, if you don't care about the order of the points in the valid array you could use an NSMutableSet instead and not worry about checking if the point is already in the collection.
And yes, recursion in methods is fine. They're really the same as C functions, just with a couple hidden arguments (the self pointer and the method name) and called through a dispatch function.

Need help with NSMutableArray solution

Im currently devising a solution for my object BuildingNode *tower which is held inside NSMutableArray *gameObjects, to attack EnemyNode *enemy objects also held inside the gameObjects array.
I have a proposed solution which occasionally causes my game to freeze (temporarily) as the solution employed is quite buggy.
My solution is that each tower object contains its own NSMutableArray *targets which is synthesized from the Tower Class. If an enemy comes into range or out of range of any given tower object, the corrosponding index of the EnemyNode *enemy object from the gameObjects array is saved as an NSNumber into the targets array, or alternatively if the enemy object is out of range, it is removed from the .targets array.
Basically the idea is that the targets array holds the indices of any enemy that is in scope of the tower.
The problem I seem to be facing is that because this tower.targets array is updated dynamically all the time, i believe that if i'm doing some sort of operation on a particular index of tower.targets that then is removed, i get this error:
-[BuildingNode distance]: unrecognized selector sent to instance 0x2dd140
Each BuildingNode *tower has different attacking alogrithms that use the tower.targets array for calling back the sorted/desired enemy.
For example, a random attack style will randomize a number between 0 & [tower.targets count] then I can create a pointer to gameObjects with the corresponding [tower.targets intValue].
Something like this:
EnemyNode *enemy = (EnemyNode *)[gameObjects objectAtIndex:[[tower.targets objectAtIndex:rand]intValue]];
So this will find a random enemy from the potential .targets array and then create a pointer object to an enemy.
I've put in many if statements to ensure that in the case of a .targets index being removed mid-sorting, the operation shouldnt go ahead, thus removing the game failure rate, but it still occurs occassionally.
Heres my code:
Please note that BuildingNode *tower == BuildingNode *build.
This is just a snippet of the iteration inside gameObjects.
//Potential Enemies (Indices) Add and Delete/
if (enemy.distance < build.atk_distance && !enemy.isExploding){
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if(![build.targets containsObject:index]){
[build.targets addObject:index];
}
}
else {
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if ([build.targets containsObject:index]){
[build.targets removeObject:index];
}
}
}
//Aiming// Nearest Algorithm.
//Will find the nearest enemy to the building
if (enemy.distance < build.atk_distance){
if (!build.isAttacking || build.atk_id == 0){
if ([build.targets count]){
if ([build.atk_style isEqualToString:#"near"]){
int l_dist;
for (int i = 0; i < [build.targets count]; i++){
//Grab the Enemy from Targets
if ([build.targets objectAtIndex:i]){
if([array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]){
EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]];
if (temp){
int c_dist = temp.distance;
if (!l_dist || c_dist < l_dist){
l_dist = c_dist;
build.atk_id = temp.uniqueID;
build.isAttacking = YES;
}
}
}
}
}
}
}}
Unrecognized selector sent = calling a method on an object that doesn't exist. In this case, you're calling the distance method/property getter on a BuildingNode object.
The only distance I see is on your temp object, which is retrieved by EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]; That indicates you are really pulling a BuildingNode object out of the array instead of an EnemyNode. Find out why that happens.

NSMutableArray insertObject atIndex issues

There are lots of examples on this site about adding to NSMutableArray and I have looked at many but I still don't either understand (highly possible) or am missing something fundamental.
I am trying to add to an NSMutableArray via a for loop. I want to keep track of button x,y coordinate position using the button tag as key/index so I can change a button as a user clicks it. This is all without IB.
My init is as follow:
self.tmpXpos = [[NSMutableArray alloc] init];
self.tmpYpos = [[NSMutableArray alloc] init];
[self.tmpXpos insertObject:[NSNull null] atIndex:0];
[self.tmpYpos insertObject:[NSNull null] atIndex:0];
**I added these after reading about 'creating' the array and populating it with null.
In the loop:
NSUInteger btag = button.tag; //NSUInterger as per help
>>NSNumber *xNumber = [NSNumber numberWithInteger:[self xpos]]; // NSNumber as an Object wrapper
>>NSNumber *yNumber = [NSNumber numberWithInteger:[self ypos]];
>>[self.tmpXpos insertObject:xNumber atIndex:btag];
>>[self.tmpYpos insertObject:yNumber atIndex:btag];
NSLog(#"%d, %#, %#",btag,xNumber,yNumber);
The log entries look ok:
2,64,0
3,128,0
etc for each button to be added to the screen.
During debugging - the tmpXpos and tmpYpos listed as " 0 Objects " under self.
I retrieve the information thusly:
NSUInteger tmpSt = [self.sTags integerValue];
NSNumber *tmpX = [self.tmpXpos objectAtIndex:tmpSt];
NSNumber *tmpY = [self.tmpYpos objectAtIndex:tmpSt];
int tmpPositionX = [tmpX intValue];
int tmpPositionY = [tmpY intValue];
NSLog(#"%d, %d",tmpPositionX, tmpPositionY);
NSLog yields 0, 0
I do appreciate any guidance or questions you folks might have as this site's 'sanity checks' have saved my bacon in the past.
I am ignoring memory leaks as of yet, but they are in the back of my mind. No worries there.
Ironically, I have a couple of other arrays that work just fine, but the exception is that they are not populated via a loop.
Lastly, the code works fine as the button at location 0,0 does change as per design, LOL, but not for any other location.
Thanks again!
Neil
I think your problem is that you are creating the NSNumbers with the method numberWithInteger, when an NSUInteger is actually an unsigned long. So try
xNumber = [NSNumber numberWithUnsignedLong:[self xpos]];
Trees for the forest answer. I wanted to populate an array with a quantity, however, the array expects that quantity to be in numerical sequence. My data didn't have any 'gaps' but wasn't in sequence either; I.E., 1,2,3,6,7,12 etc. So, as user397313 helped me see, NSDictionary is the way to go with that kind of datum.

Best way to implement multiple levels within Objective-C roguelike?

I'm working on a roguelike using Objective-C/Cocoa to learn more. I've gotten most of the basic functionality out of the way, but I still have one problem I've been trying to figure out.
Here's a breakdown of the process:
First, the map is loaded:
NSString* mapPath = [[NSBundle mainBundle] pathForResource:mapFileName ofType:mapFileType];
NSURL* mapURL = [NSURL fileURLWithPath: mapPath];
currentMap_ = [[Map alloc] initWithContentsOfURL: mapURL];
worldArray = [[NSMutableArray alloc] init];
itemArray = [[NSMutableArray alloc] init];
[self populateMap];
return;
Then, in the populateMap function, it goes through each cell of the loaded map, using NSPoints and a loop, and creates objects based on the data from the map in WorldArray. For items, normal floor is put in where the item is, and an item is then made in itemArray. Both arrays are 30x30, as determined by the height of the map.
Here is the populateMap code:
- (void)populateMap
{
NSPoint location;
for ( location.y = 0; location.y < [currentMap_ height]; location.y++ )
{
for ( location.x = 0; location.x < [currentMap_ width]; location.x++ )
{
char mapData = [currentMap_ dataAtLocation: location];
for ( GameObject *thisObject in worldDictionary )
{
//NSLog(#"char: <%c>", [thisObject single]);
if ( mapData == [thisObject single])
{
NSString* world = [thisObject className];
//NSLog(#"(%#) object created",thisObject);
[self spawnObject:world atLocation:location];
}
}
for ( Item *thisObject in itemDictionary )
{
//NSLog(#"char: <%c>", [thisObject single]);
if ( mapData == [thisObject single] )
{
NSString* item = [thisObject className];
NSString* floor = [NormalFloor className];
//NSLog(#"(%#) object created",thisObject);
[self spawnItem:item atLocation:location];
[self spawnObject:floor atLocation:location];
}
}
if ( mapData == '1'
&& [player_ stepsTaken] <= 0)
{
//NSLog(#"player spawned at (%f, %f)",location.x,location.y);
player_ = [[Player alloc] initAtLocation: location];
}
if ( mapData == '1' )
{
//NSLog(#"floor created at (%f, %f)",location.x,location.y);
[worldArray addObject:[[NormalFloor alloc] initAtLocation: location]];
}
}
}
[self setNeedsDisplay:YES];
}
This is what is called when things are spawned:
- (void)spawnObject: (NSString*) object atLocation: (NSPoint) location
{
//NSLog(#"(%#) object created",thisObject);
[worldArray addObject:[[NSClassFromString(object) alloc] initAtLocation: location]];
}
- (void)spawnItem: (NSString*) item atLocation: (NSPoint) location
{
//NSLog(#"(%#) object created",thisObject);
[itemArray addObject:[[NSClassFromString(item) alloc] initAtLocation: location]];
}
worldArray and itemArray are what the game works on from that moment onwards, including the drawing. The player is inside of worldArray as well. I'm considering splitting the player into another array of characterArray, to make it easier when I add things like monsters in the not so distant future.
Now, when I load a new level, I had first considered methods like saving them to data and loading them later, or some sort of savestate function. Then I came to the realization that I would need to be able to get to everything at the same time, because things can still happen outside of the player's current scope, including being chased by monsters for multiple floors, and random teleports. So basically, I need to figure out a good way to store worldArray and itemArray in a way that I will be able to have levels of them, starting from 0 and going onward. I do need a savestate function, but there's no point touching that until I have this done, as you shouldn't actually be allowed to save your game in roguelikes.
So to reiterate, I need to have one set of these arrays per level, and I need to store them in a way that is easy for me to use. A system of numbers going from 0-upward are fine, but if I could use something more descriptive like a map name, that would be much better in the long run.
I've figured out my problem, I'm using an NSMutableDictionary for each and storing them with the keys that correspond to each level. Works like a charm. Bigger problems elsewhere now.
I figured it out, I'm using NSMutableDictionaries, one for each array (objects, items, eventually characters). They're stored using the name of the level. Works like a charm.