Strangest thing I've seen yet.. NSLog is failing within a method based on something bizarre. Here is the code:
-(void) testBoard {
BoardManager* bm = [BoardManager manager];
Board* board = [bm boardForName:#"fourteen by eight"];
NSLog(#"%#", board);
NSLog(#"%#", board.coords);
}
in Board.m :
-(NSArray*) coords {
if(!_coords.count && _definition) {
NSArray* c = [Board decode:_definition];
[self setCoords:c];
}
return _coords;
}
+(NSArray*) decode:(NSString*)encodedCoords {
NSMutableArray* coords = [NSMutableArray array];
NSArray* tokens = [encodedCoords componentsSeparatedByString:#","];
int i = 0;
NSString* dimStr = [tokens objectAtIndex:i++];
int width = [dimStr substringToIndex:2].intValue;
int height = [dimStr substringWithRange:NSMakeRange(2, 2)].intValue;
int depth = [dimStr substringFromIndex:4].intValue;
NSLog(#"w=%d h=%d d=%d", width, height, depth);
NSString* b128;
NSString* b2;
for(int z=0; z<depth; z++) {
for(int y=0; y<height; y++) {
b128 = [tokens objectAtIndex:i++];
NSLog(#"[%#]", b128);
b2 = [Board base128to2:b128];
NSLog(#"b2=%#",b2);
for(int x=0; x<b2.length; x++) {
if([b2 characterAtIndex:b2.length-1-x] == '1') {
Coord* coord = [Coord x:width-1-x y:height-1-y z:z];
[coords addObject:coord];
}
}
}
}
return coords;
}
Now what happens is, none of the NSLog statements within decode: or methods called from decode: will log to the console. However, NSLog before and after calling decode: work, and the rest of the code around the NSLog's executes fine. I found that I could get all the NSLog statements to work simply by commenting out [coords addObject:coord];. This statement appears after NSLog statements that it is affecting. I also found I could get the NSLog's to work by rearranging a few lines in testBoard: like this:
-(void) testBoard
{
BoardManager* bm = [BoardManager manager];
Board* board = [bm boardForName:#"fourteen by eight"];
NSLog(#"%#", board);
NSString* def = board.definition;
NSArray* coords = [Board decode:def];
NSLog(#"%#", coords);
}
This seems quite bizarre..an xcode Loch Ness monster surfacing?!
Have you tried running in the debugger? What happens when you get to that if statement
if(!_coords.count && _definition)
Are you sure _coords.count is 0 and _definition is not nil
In my experience, NSLog macro expansions can fail. Most of the time it's obvious why, sometimes not.
Just do something like this:
id objToLog = /* whatever */;
NSLog(#"%#", objToLog);
If this approach works, you can go ahead with your development and maybe you figure the problem out later.
Related
In my app I'me getting responses from the server and I have to check that I don't create duplicate objects in the NSArray which contains NSDictionaries. Now to check if the objects exists I do this:
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
}
if (i == appDelegate.currentUser.userSiteDetailsArray.count - 1 && ![[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier] && needToCheck){
// It means it's the last object we've iterated through and needToCheck is still = YES;
//Doing stuff here
}
}
I set up a BOOL value because this iteration goes numerous times inside a method and I can't use return to stop it. I think there is a better way to perform this check and I would like to hear your suggestions about it.
BOOL needToCheck = YES;
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
break;
}
}
if (needToCheck) {
//Doing stuff here
}
But, as others have said, you can maybe keep a "summary" in a separate NSSet that you check first, vs spinning through all the dictionaries.
NSDictionary *previousThing = nil;
for (NSDictionary *thing in appDelegate.currentUser.userSiteDetailsArray) {
if ([thing[#"webpropID"] isEqualToString:newWebPropertyIdentifier]) {
previousThing = thing;
break;
}
}
if (previousThing == nil) {
// no previous thing
} else {
// duplicate
}
EDIT: All array used in my project are NSMutableArray class
An overview of what I want to do is from my selectClueView, user can select a number from 3-10. Which represent the number of clue they will play. It will then generate list of random number between 0 and the objectArray.count and add the NSNumber into another array known as dataArray. Everything is working fine including prepareForSegue which transfer SelectClueViewController.dataArray to GamePageViewController.clueToSelect
However, I am stuck with loading data into the new array ds, from an array that hold all the object allDataObject. I am fairly new to iOS and because I had a working function in c#, I tried to replicate it in objective-C, unfortunately it seems that I can't replicate it fully.
In short, I'm trying to add data from allDataObject array into ds array with NSNumber values from cluesToSelect array.
Below are the coding which are used. Any help to fix the issue would be much appreciated. If there are any more information that I should give, please let me know.
SelectClueViewController.m
- (IBAction)onGoPress:(id)sender {
[self chooseNumber];
NSLog(#"Array got %d numbers",dataArray.count);
}
-(void)chooseNumber
{
[dataArray removeAllObjects];
maxCount = [numberOfClues.text intValue];
int count = 0;
do {
NSInteger rdmNumber = arc4random()%objectArray.count;
if (![dataArray containsObject:[NSNumber numberWithInt:rdmNumber]])
{
NSNumber* number = [NSNumber numberWithInt:rdmNumber];
[dataArray addObject:number];
count++;
NSLog(#"random no - %d",rdmNumber);
}
} while (count < maxCount);
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"sendNumber"]) {
GamePageViewController *gpViewController = [segue destinationViewController];
gpViewController.cluesToSelect = self.dataArray;
NSLog(#"Success");
}
}
GamePageViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
daoDS = [[ClueDataDAO alloc]init];
self.allDataObject = daoDS.PopulateDataSource;
NSLog(#"%d",cluesToSelect.count);
[self fillDataSample];
//for keyboard
self.answer.delegate = self;
}
-(void)fillDataSample
{
int count = 0;
do {
// [self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count]intValue] ]];
ds = [[NSMutableArray alloc]init];
currentClueData = [[ClueData alloc]init];
int firstIndex = [[cluesToSelect objectAtIndex:count]intValue];
currentClueData = [allDataObject objectAtIndex:firstIndex];
[ds addObject:currentClueData];
count++;
} while (count < cluesToSelect.count);
NSLog(#"ds got %d object",ds.count);
}
EDIT:
I am now able to make it add in object into the ds array, unfortunately it only add once. Can someone look at my fillDataSample function?
In the line you point at at the beginning :
[self.ds addObject:[allDataObject objectAtIndex:[cluesToSelect objectAtIndex:count]]];
Just a wild guess, but [cluesToSelect objectAtIndex:count] returns an object. You're trying to pass that to another objectAtIndex:, which takes an int as argument. I'm guessing the error could come from that. You could try using .intValue if it's a number.
Edit :
Here's something more readable, considering cluesToSelect and allDataObject contain only NSNumbers :
int firstIndex = [[cluesToSelect objectAtIndex:count] intValue];
int secondIndex = [[allDataObject objectAtIndex: firstIndex] intValue];
[self.ds addObject:secondIndex];
As per our discussion,
Kindly check this one:
[self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count] intValue]]];
will do your work.
The reason as to why it didn't work for my new method of loading object from allClueData to a class object currentClueData to ds was because I did not nil and alloc init the class object after adding it to ds. The object were unable to overwrite their own value like they do in other language. (which was probably why I'm wrecking my brain for doing it in objective C) But after adding in nil the object and casting alloc and init, its working great now. thanks all :)
-(void)fillDataSample
{
int count = 0;
currentClueData = [[ClueData alloc]init];
ds = [[NSMutableArray alloc]init];
do {
// [self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count]intValue] ]];
int firstIndex = [[cluesToSelect objectAtIndex:count]intValue];
currentClueData = [allDataObject objectAtIndex:firstIndex];
[ds addObject:currentClueData];
currentClueData =nil;
currentClueData = [[ClueData alloc]init];
count++;
} while (count < cluesToSelect.count);
NSLog(#"ds got %d object",ds.count);
}
i have some code that requires the use of a for loop to read variables from an array.
int size=sizeof names;
NSLog(#"thelast one is %d",size);
NSString *usersName=userName.text;
NSString *usersPass=passWord.text;
for (i=0; i<=size;i++){
NSString *namesArray=[names objectAtIndex:i];
NSString *passArray=[pass objectAtIndex:i];
NSLog(#"namesArray %#",namesArray);
NSLog(#"passArray %#",passArray);
if([namesArray isEqualToString:usersName]){
userValid=1;
NSLog(#"The content of arry4 is %#",namesArray);
}
if([passArray isEqualToString:usersPass]){
passValid=1;
NSLog(#"The content of arry4 is %#",passArray);
}
else {
userValid=0;
passValid=0;
}
}
I've been having some problems because every time this function is called from within the program, it's almost as if the 'sizeof names' is wrong, therefore not all values in the array are checked.
I'm generally a Java programmer so i'm used to names.length, and i was told sizeof names is essentially the same thing... any help?
Cheers.
Don't use sizeof. Use [names count].
You want to use [names count] not sizeof names. Sizeof is going to give you the size of the actual names object pointer itself and not the number of elements, since it's dynamic memory type.
To get the number of elements stored in an NSAarray you should use the instance method count, which returns an NSUInteger.
Alternatevely, you can iterate over these elements using the for in loop, which is available also in Java, if I recall correctly.
for (MyClass *element in myArray) {
NSLog(#"%#", element);
}
Note that sizeof is a C operator that returns the size in bytes of its operand, so it doesn't tell you how many elements are stored in that NSArray, but the size in bytes of one NSArray instance.
I know your question has already been answered - but here is a more Cocoa way of writing it
NSString *userName = userName.text;
NSString *userPass = passWord.text;
// Use a block enumerator
NSUInteger nameIdx = [names indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return ([obj isEqualToString:userName]);
}];
// Is the name in the array
if (nameIdx == NSNotFound) {
// Name not in array - so set to zero
userValid = 0;
passValid = 0;
} else {
userValid = 1;
// See if the corresponding password is correct
NSString password = [pass objectAtIndex:nameIdx];
if (![password isEqualToString:userPass]) {
passValid = 0;
} else {
passValid = 1;
}
One can also use Fast Enumeration, in some cases it can be more clear to a reader:
NSString *userName = userName.text;
NSString *userPass = passWord.text;
BOOL userValid = NO;
BOOL passValid = NO;
int index = 0;
for (NSString *eachName in namesArray) {
if ([eachName isEqualToString:userName) {
userValid = YES:
if ([[passArray objextAtIndex:index] isEqualToString:passWord) {
passValid = YES;
}
break;
}
index += 1;
}
I am developing an application for the iPhone. The question I have is how to display a new label with a different text every .5 seconds. For example, it would display Blue, Red, Green, Orange and Purple; one right after one another. Right now I am doing this:
results = aDictionary;
NSArray *myKeys = [results allKeys];
NSArray *sortedKeys = [myKey sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
int keyCount = [sortedKeys count];
while (flag == NO) {
NSTimeInterval timeMS = [startDate timeIntervalSinceNow] * -10000.0;
if (timeMS >= i) {
ii++;
i += 1000;
NSLog(#"endDate = %f", timeMS);
int randomNumber = rand() % keyCount + 1;
lblResult.text = [results valueForKey:[sortedKeys objectAtIndex:(randomNumber - 1)]];
result = [results valueForKey:[sortedKeys objectAtIndex:(randomNumber - 1)]];
lblResult.text = result;
}
if (ii > 25) {
flag = YES;
}
}
lblResult.text = [results valueForKey:[sortedKeys objectAtIndex:(sortedKeys.count - 1)]];
this function is called at the viewDidAppear Function and currently isn't displaying the new labels. It only displays the one at the end. Am I doing anything wrong? What would be the best method to approach this?
The problem is that you're not giving the run loop a chance to run (and therefore, drawing to happen). You'll want to use an NSTimer that fires periodically and sets the next text (you could remember in an instance variable where you currently are).
Or use something like this (assuming that items is an NSArray holding your strings):
- (void)updateText:(NSNumber *)num
{
NSUInteger index = [num unsignedInteger];
[label setText:[items objectAtIndex:index]];
index++;
// to loop, add
// if (index == [items count]) { index = 0; }
if (index < [items count]) {
[self performSelector:#selector(updateText:) withObject:[NSNumber numberWithUnsignedInteger:index] afterDelay:0.5];
}
}
At the beginning (e.g. in viewDidAppear:), you could then call
[self updateText:[NSNumber numberWithUnsignedInteger:0]];
to trigger the initial update.
You'd of course need to ensure that the performs are not continuing when your view disappears, you could do this by canceling the performSelector, or if you're using a timer, by simply invalidating it, or using a boolean, or ...
And if you want to get really fancy, use GCD :)
my first question on Stackoverflow.
Let me start with a bit of code. It's a bit repetitive so I'm going to cut out the parts I repeat for different arrays (feel free to ask for the others). However, please ignore the code in preference to answering the Qs at the bottom. Firstly: thank you to answerers in advance. Secondly: the freeing of data.
#implementation ES1Renderer
GLfloat **helixVertices;
GLushort **helixIndices;
GLubyte **helixColors;
- (void)freeEverything
{
if (helixVertices != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixVertices[i]);
free(helixVertices);
}
if (helixIndices != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixIndices[i]);
free(helixIndices);
}
if (helixColors != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixColors[i]);
free(helixColors);
}
}
(I will get to the calling of this in a moment). Now for where I malloc() the arrays.
- (void)askForVertexInformation
{
int nrows = self.helper.numberOfAtoms / 300;
int mrows = [self.helper.bonds count] / 300;
int alphaCarbonRows = [self.helper.alphaCarbons count] / 30;
helixVertices = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
helixIndices = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
helixColors = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
for (int i=0; i < alphaCarbonRows + 1; i++)
{
helixVertices[i] = malloc(sizeof(helixVertices) * HELIX_VERTEX_COUNT * 3 * 33);
helixIndices[i] = malloc(sizeof(helixIndices) * HELIX_INDEX_COUNT * 2 * 3 * 33);
helixColors[i] = malloc(sizeof(helixColors) * HELIX_VERTEX_COUNT * 4 * 33);
}
[self.helper recolourVerticesInAtomRange:NSMakeRange(0, [self.helper.alphaCarbons count]) withColouringType:CMolColouringTypeCartoonBlue forMasterColorArray:helixColors forNumberOfVertices:HELIX_VERTEX_COUNT difference:30];
self.atomsToFree = self.helper.numberOfAtoms;
self.bondsToFree = [self.helper.bonds count];
self.alphasToFree = [self.helper.alphaCarbons count];
}
Finally, the bit which calls everything (this is a separate class.)
- (void)loadPDB:(NSString *)pdbToLoad
{
if (!self.loading)
{
[self performSelectorOnMainThread:#selector(stopAnimation) withObject:nil waitUntilDone:YES];
[self.renderer freeEverything];
[renderer release];
ES1Renderer *newRenderer = [[ES1Renderer alloc] init];
renderer = [newRenderer retain];
[self performSelectorOnMainThread:#selector(stopAnimation) withObject:nil waitUntilDone:YES]; // need to stop the new renderer animating too!
[self.renderer setDelegate:self];
[self.renderer setupCamera];
self.renderer.pdb = nil;
[renderer resizeFromLayer:(CAEAGLLayer*)self.layer];
[newRenderer release];
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(setup:) object:pdbToLoad];
[self.queue addOperation:invocationOperation];
[invocationOperation release];
}
}
- (void)setup:(NSString *)pdbToLoad
{
self.loading = YES;
[helper release];
[renderer.helper release];
PDBHelper *aHelper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
helper = [aHelper retain];
renderer.helper = [aHelper retain];
[aHelper release];
if (!resized)
{
[self.helper resizeVertices:11];
resized = YES;
}
self.renderer.helper = self.helper;
[self.helper setUpAtoms];
[self.helper setUpBonds];
if (self.helper.numberOfAtoms > 0)
[self.renderer askForVertexInformation];
else
{
// LOG ME PLEASE.
}
[self performSelectorOnMainThread:#selector(removeProgressBar) withObject:nil waitUntilDone:YES];
[self performSelectorOnMainThread:#selector(startAnimation) withObject:nil waitUntilDone:YES];
self.renderer.pdb = pdbToLoad;
self.loading = NO;
}
What I'm doing here is loading a molecule from a PDB file into memory and displaying it on an OpenGL view window. The second time I load a molecule (which will run loadPDB: above) I get the Giant Triangle Syndrome and Related Effects... I will see large triangles over my molecule.
However, I am releasing and reallocating my PDBHelper and ES1Renderer every time I load a new molecule. Hence I was wondering:
1. whether the helixVertices, helixIndices and helixColors which I have declared as class-wide variables are actually re-used in this instance. Do they point to the same objects?
2. Should I be setting all my variables to NULL after freeing? I plan to do this anyway, to pick up any bugs by getting a segfault, but haven't got round to incorporating it.
3. Am I even right to malloc() a class variable? Is there a better way of achieving this? I have no other known way of giving this information to the renderer otherwise.
I can't answer your general questions. There's too much stuff in there. However, this caught my eye:
[helper release];
[renderer.helper release];
PDBHelper *aHelper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
helper = [aHelper retain];
renderer.helper = [aHelper retain];
[aHelper release];
I think this stuff possibly leaks. It doesn't make sense anyway.
If renderer.helper is a retain or copy property, do not release it. It already has code that releases old values when it is assigned new values. Also do not retain objects you assign to it.
You have alloc'd aHelper, so there's no need to retain it again. The above code should be rewritten something like:
[helper release];
helper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
renderer.helper = helper;
Also, I think your helix malloced arrays should probably be instance variables. As things stand, if you have more than one ES1Renderer, they are sharing those variables.