How do you set a label text as something from an NSMutableArray? - xcode

I am trying to understand how to set a label to be the text from an array when you press the button. When I press the button, the label disappears, and then nothing comes up. No crashes in code.
Relevant code:
-(void)setupArray {
wordArray = [[NSMutableArray alloc] init];
[wordArray addObject:#"test1"];
[wordArray addObject:#"test2"];
[wordArray addObject:#"test3"];
}
- (IBAction)start:(id)sender {
int value = (arc4random() % 3) + 1;
self.typeThis.text = [self.wordArray objectAtIndex:value];
}
typeThis is the label name, and I think I have hooked up everything already, i.e. set up the buttons/delegates/etc...I don't understand why it isn't working. Can anybody help?

considering you have bound everything properly and you are not under ARC. Here is a thing that might cause you the issue.
when you are allocating wordArray you can try using following code snippet.
NSMutableArray tempArray = [[NSMutableArray alloc] init];
self.wordArray = tempArray;
[tempArray release];
if you are under ARC you can try self.wordArray = [NSMutableArray array];
then add objects to self.wordArray i.e.[self.wordArray addObject:#"test1"];. Here is some explanation about arc4random().
EDIT :
Here's a public spec for Automatic Reference Counting and a quote from the public iOS 5 page:
Automatic Reference Counting (ARC) for Objective-C makes memory
management the job of the compiler. By enabling ARC with the new Apple
LLVM compiler, you will never need to type retain or release again,
dramatically simplifying the development process, while reducing
crashes and memory leaks. The compiler has a complete understanding of
your objects, and releases each object the instant it is no longer
used, so apps run as fast as ever, with predictable, smooth
performance.
It is possible to detect if ARC is enabled. Simply add the following snippet to any file that requires ARC.
#ifndef __has_feature
#define __has_feature(x) 0 /* for non-clang compilers */
#endif
#if !__has_feature(objc_arc)
#error ARC must be enabled!
#endif
More info :
http://clang.llvm.org/docs/LanguageExtensions.html#__has_feature_extension
HTH.

Your '+1' is giving you a result between 1 and 3, and your indexes are from 0 to 2, so I'd expect it to go wrong one time in 3.

Is this under ARC? If so, is wordArray declare as strong?

Related

When to retain and release CGLayerRef?

I have a question similar to this one:
CGLayerRef in NSValue - when to call retain() or release()?
I am drawing 24 circles as radial gradients in a view. To speed it up I am drawing the gradient into a layer and then drawing the layer 24 times. This worked really well to speed up the rendering. On subsequent drawRect calls some of the circles may need to be redrawn with a different hue, while others remain the same.
Every time through drawRect I recalculate a new gradient with the new hue and draw it into a new layer. I then loop through the circles, drawing them with the original layer/gradient or new layer/gradient as appropriate. I have a 24 element NSMutableArray that stores a CGLayerRef for each circle.
I think this is the answer provided in the question I linked above, however it is not working for me. The second time through drawRect, any circle that is drawn using the CGLayerRef that was stored in the array causes the program to crash when calling CGContextDrawLayerAtPoint. In the debugger I have verified that the actual hex value of the original CGLayerRef is stored properly into the array, and in the second time through drawRect that the same hex value is passed to CGContextDrawLayerAtPoint.
Further, I find that if I don't CGLayerRelease the layer then the program doesn't crash, it works fine. This tells me that something is going wrong with the memory management of the layer. It's my understanding that storing an object into an NSArray will increment it's reference count, and it won't be deallocated until the array releases it.
Anyway, here is the relevant code from drawRect. Down at the bottom you can see that I commented out CGLayerRelease. In this configuration the app doesn't crash although I think this is a resource leak. If I uncomment that release then the app crashes the second time though drawRect (between the first and second calls one of the circles has it's led_info.selected property cleared, indicating that it should use the saved layer rather than the new layer:
NSLog(#"ledView drawing hue=%4f sat=%4f num=%d size=%d",hue_slider_value,sat_slider_value,self.num_leds,self.led_size);
rgb_color = [UIColor colorWithHue:1.0 saturation:1.0 brightness:1.0 alpha:1.0];
end_color = [UIColor colorWithHue:1.0 saturation:1.0 brightness:1.0 alpha:0.0];
NSArray *colors = [NSArray arrayWithObjects:
(id)rgb_color.CGColor, (id)end_color.CGColor, nil];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,(__bridge CFArrayRef) colors, NULL);
CGLayerRef layer = CGLayerCreateWithContext(context, (CGSize){self.led_size,self.led_size}, /*auxiliaryInfo*/ NULL);
if (layer) {
CGContextRef layer_context = CGLayerGetContext(layer);
CGContextDrawRadialGradient(layer_context, gradient, led_ctr,self.led_size/8,led_ctr, self.led_size/2,kCGGradientDrawsBeforeStartLocation);
} else {
NSLog(#"didn't get a layer");
}
for (int led=0;led<[self.led_info_array count];led++) {
led_info=[self.led_info_array objectAtIndex:led];
// the first time through selected=1 and led_info.cg_layer=nil for all circles,
// so this branch is taken.
if (led_info.selected || led_info.cg_layer==nil) {
CGPoint startPoint=led_info.rect.origin;
CGContextDrawLayerAtPoint(context, startPoint, layer);
CGContextAddRect(context, led_info.rect);
led_info.cg_layer=layer;
// the second time through drawRect one or more circles have been deselected.
// They take this path through the if/else
} else {
CGPoint startPoint=led_info.rect.origin;
// app crashes on this call to CGContextDrawLayerAtPoint
CGContextDrawLayerAtPoint(context, startPoint, led_info.cg_layer);
}
}
// with this commented out the app doesn't crash.
//CGLayerRelease(layer);
Here is the declaration of led_info:
#interface ledInfo : NSObject
#property CGFloat hue;
#property CGFloat saturation;
#property CGFloat brightness;
#property int selected;
#property CGRect rect;
#property CGPoint center;
#property unsigned index;
#property CGLayerRef cg_layer;
- (NSString *)description;
#end
led_info_array is the NSMutableArray of ledInfo objects, the array itself is a property of the view:
#interface ledView : UIView
#property float hue_slider_value;
#property float sat_slider_value;
#property unsigned num_leds;
#property unsigned led_size;
#property unsigned init_has_been_done;
#property NSMutableArray *led_info_array;
//#property layerPool *layer_pool;
#end
The array is initialized like this:
self.led_info_array = [[NSMutableArray alloc] init];
Edit: since I posted I have found that if I put retain/release around the assignemt into the NSMutableArray then I can also leave in the original CGLayerRelease and the app works. So I guess this is how it is supposed to work, although I'd like to know why the retain/release is necessary. In the objective C book I am reading (and the answer to the question linked above) I thought assigning into NSArray implicitly did retain/release. The new working code looks like this:
if (led_info.selected || led_info.cg_layer==nil) {
CGPoint startPoint=led_info.rect.origin;
CGContextDrawLayerAtPoint(context, startPoint, layer);
CGContextAddRect(context, led_info.rect);
if (led_info.cg_layer) CGLayerRelease(led_info.cg_layer);
led_info.cg_layer=layer;
CGLayerRetain(layer);
} else {
CGPoint startPoint=led_info.rect.origin;
CGContextDrawLayerAtPoint(context, startPoint, led_info.cg_layer);
}
You can probably tell that I'm brand new to Objective C and iOS programming, and I realize that I'm not really sticking to convention regarding case and probably other things. I'll clean that up but right now I want to solve this memory management problem.
Rob, thanks for the help. I could use a little further clarification. I think from what you are saying that there are two problems:
1) Reference counting doesn't work with CGLayerRef. OK, but it would be nice to know that while writing code rather than after debugging. What is my indication that when using "things" in Objective C/cocoa that resource counting doesn't work?
2) You say that I'm storing to a property, not an NSArray. True, but the destination of the store is the NSArray via the property, which is a pointer. The value does make it into the array and back out. Does resource counting not work like this? ie instead of CGLayerRef, if I were storing some NSObject into NSArray using the code above would resource counting work? If not, then would getting rid of the intermediate led_info property and accessing the array directly from within the loop work?
You're not storing the layer directly in an NSArray. You're storing it in a property of your ledInfo object.
The problem is that a CGLayer is not really an Objective-C object, so neither ARC nor the compiler-generated (“synthesized”) property setter will take care of retaining and releasing it for you. Suppose you do this:
CGLayerRef layer = CGLayerCreateWithContext(...);
led_info.cg_layer = layer;
CGLayerRelease(layer);
The cg_layer setter method generated by the compiler just stores the pointer in an instance variable and nothing else, because CGLayerRef isn't an Objective-C object reference. So when you then release the layer, its reference count goes to zero and it's deallocated. Now you have a dangling pointer in your cg_layer property, and when you use it later you crash.
The fix is to write the setter manually, like this:
- (void)setCg_layer:(CGLayerRef)layer {
CGLayerRetain(layer);
CGLayerRelease(_cg_layer);
_cg_layer = layer;
}
Note that it's important to retain the new value before releasing the old one. If you release the old one before retaining the new one, and the new one happens to be the same as the old one, you might deallocate the layer right in the middle!
UPDATE
In response to your edits:
Reference counting works with CGLayerRef. Automatic reference counting (ARC) doesn't. ARC only works with things that it thinks are Objective-C objects ARC does not automatically retain and release a CGLayerRef, because ARC doesn't think a CGLayerRef is a reference to an Objective-C object. An Objective-C object is (generally speaking) an instance of a class declared with #interface, or a block.
The CGLayer Reference says that CGLayer is derived from CFType, the basic type for all Core Foundation objects. (As far as ARC is concerned, a Core Foundation object is not an Objective-C object.) You need to read about “Ownership Policy” and “ Core Foundation Object Lifecycle Management” in the Memory Management Programming Guide for Core Foundation.
The “destination of the store” is an instance variable in your ledInfo object. It's not “the NSArray via the property”. The value doesn't ”make it into the array and back out.” The array gets a pointer to your ledInfo object. The array retains and releases the ledInfo object. The array never sees or does anything with the CGLayerRef. Your ledInfo object is responsible for retaining and releasing any Core Foundation objects it wants to own, like the layer in its cg_layer property.
As I mentioned, if ledInfo doesn't retain the layer (with CFRetain or CGLayerRetain) in its cg_layer setter, it risks the layer being deallocated, leaving the ledInfo with a dangling pointer. Do you understand what a dangling pointer is?

XCODE - How to update array?

I'm very new to Xcode/programming and trying to modify existing code
I'm having a small problem where I have an amount of objects (enemies) on the screen at one particular time and cannot redefine their value. I set my enemies to begin with 3 on the screen.
My objective is to change the amount of enemies based on the current score.
I've attached snippets of the code below.
int numberOfEnemies;
if (self.score>=0) {
numberOfEnemies = 3
}
else if (self.score>=100) {
numberOfEnemies = 4
}
// Setup array
enemyArray = [[NSMutableArray alloc] init];
for(int i = 0; i < numberOfEnemies; i++) {
[enemyArray addObject:[SpriteHelpers setupAnimatedSprite:self.view numFrames:3
withFilePrefix:#"enemyicon" withDuration:((CGFloat)(arc4random()%2)/3 + 0.5)
ofType:#"png" withValue:0]];
}
enemyView = [enemyArray objectAtIndex:0];
What do I need to do to parse the new value of numberOfEnemies into the array when my score updates?
I'm going to move our conversation into an answer since I don't want it to get too long winded, and I can easily edit and expand on this.
So far, we've established that the reason that you're having issues is that you execute the above code in the viewDidLoad function, which will run at least once when the application is first started. The problem with this is as you've found out, that you arent getting a chance to see a new score, and then update the number of enemies.
I know that game update loops for iOS are usually done in the following structure, but I would recommend finding a tutorial online to get what may be a more efficient/correct way to do it.
From your current structure, I would take the code you have above and create a new function out of it:
-(void) updateDifficulty:(NSTimer *)gameTimer
{
//This can be the code you have above for now
}
Afterwards, inside of your viewDidLoad, I would put the following code:
-(void) viewDidLoad:
{
gameTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(updateDifficulty:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:gameTimer forMode:NSDefaultRunLoopMode];
}
What that does is it declares a timer that will keep track of the game time, and with how it was declared, every 1 second it will call the updateDifficulty method. This is the general structure that you want, but again I would highly suggest you check out a game tutorial from Ray Wenderlich for example.
Hope that helps!

How can I resolve my CoreGraphics Memory Leak without crashing?

I have a code iterator that is a test function for conversion of raw data to UIImage objects for file storage. I have two failure paths or options. I can fail:
A.)By having a leak (instruments doesn't show it--instruments all allocations seems to be stable, I have to iterate a number of times say 50 and it will crash running the test function through instruments or normally). Memory appears to be the culprit due to "b" option below.
B.)I can do a forced release of the CG object within UIImage in which case the app will proceed. Once the iterator completes the test will crash on the very last iteration trying to free the CGImage underneath the image returned from my sub-function. The iterator will complete it's run no matter how many iterations I request. This would point to the fact I have fixed a leak. So now I'm trying to puzzle together what is special about the very last iteration? (ARC?)
- (UIImage *) convertRGBToUIImage:(unsigned char *) buffer
withWidth:(int) width
withHeight:(int) height
{
CGColorSpaceRef colorSpace = nil;
CGContextRef context = nil;
CGImageRef ref = nil;
UIImage *retImg = nil;
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
NSLog(#"ERROR: allocating color space convert Bitmap\n");
return nil;
}
context = CGBitmapContextCreate (buffer, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace );
if (context == NULL)
{
NSLog(#"ERROR: Context not created!");
return nil;
}
ref = CGBitmapContextCreateImage(context);
CGContextRelease(context);
retImg = [UIImage imageWithCGImage:ref];
CGImageRelease(ref);
return retImg;
}
iterator basics
for(int i = 0; i < numberiterations; i++)
{
UIImage *completedImage = [phelper convertRGBToUIImage:pBuffer withWidth:width withHeight:height]
//...save it out or display it
//Turn on one of the scenarios below
//scenario "A" -- will leak memory (instruments doesn't seem to turn anything up)
//ARC free
//completedImage = nil; //this will leak and fail after ~50 iterations
//-or-
//scenario "B" -- will crash on the very last iteration every time but can run x times
//CGImageRelease([completedImage CGImage];
}
The analysis tool in Xcode prefers scenario 'A' above. Again I can do this and everything seems great but it will not successfully complete the test. I think this points to ARC. I have tried to figure out the __bridge type casting with no success. I can't seem to get the syntax right and perhaps its not the answer to my problem. Any hints or ideas would be very appreciated.
The reason why Instruments is not showing a leak is that — technically — there is none
What you are seeing is the memory pressure from an #autoreleasepool that is not tight enough:
Your method convertRGBToUIImage:withWidth:withHeight: returns a new autoreleased image, but the closest #autoreleasepool is outside of your loop.
That means that all of those images are not disposed of until the method, in which your loop lives, returns.
This is the reason why you see your memory consumption growing with each iteration in scenario A and your application crash after the loop in scenario B:
UIImage is — mostly — an Objective-C wrapper around CGImageRef so that your additional CGImageRelease in scenario B pulls the backing store out from under the UIImage’s feet. This backing store happens to be the memory hungry part of the UIImage so your drastic increase in memory consumption almost vanishes.
When the #autoreleasepool is drained after the method with the loop returned, it disposes of all the temporary UIImage instances which — in turn — want to dispose of their backing-CGImageRef…and boom there goes the program.
The trick is to create your own #autoreleasepool inside the loop:
for (int i = 0; i < numberiterations; i++) {
#autoreleasepool {
// your old iteration body comes here
}
}
Now, scenario A will behave as expected and scenario B will crash immediately at the end of the first iteration — which is to be expected, as well.
And now for some serious brain-f**k:
If you run your original code under ARC in a “Release” scheme, (i.e. with the compiler flag -Os aka “fastest smallest”) scenario A will most likely not crash!
The reason for this seemingly bizarre behavior is, that ARC garnishes your code with a special set of C-functions that perform the actual memory management — and because these functions are constant (which method calls in Objective-C aren’t necessarily) the compiler can optimize certain combinations of them away.
This leads to the UIImage never being inserted into an autorelease-pool in the first place and, thus, being disposed of after each iteration of the loop.

Iphone app wont run after upgrading to iOS 4.2

After installing Xcode 3.2.5 iOS 4.2 an app that was perfectly working stopped working. I have seen that this has happened to others but cant understand how to solve it.
My questions are:
1. How can I know where it is crashing?
2. What can I do to better debug and pinpoint the problem.
Here is the call stack.
Tommy thanks for the answer. I have build and run and suggested but when it crashes it does not shows where it crashed. Also I added a breakpoint inside the method but it does not stops there. Any further ideas? Here is the code snippet.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
ApplicationData *appdata = [ApplicationData sharedInstance];
if(appdata.currentCategoryIndex >= 0) {
Category *category = [appdata.categoryList objectAtIndex:appdata.currentCategoryIndex];
if(category.records) {
[lab_norecords setHidden:YES];
} else {
[lab_norecords setHidden:NO];
return 0;
}
return [category.records count];
}
return 0;
}
Now I am getting this error:
I believe the source of the problem is here in the same subCategoryView:
- (id)init {
if([[NSBundle mainBundle] loadNibNamed:#"SubCategoryView" owner:self options:nil]) {
//if(self = [super initWit])
cellowner = [[TableCellOwner alloc] init];
[listTableView setBackgroundColor:[UIColor clearColor]];
[listTableView setSeparatorColor:[UIColor whiteColor]];
[listTableView initialize];
}
return self;
}
From the trace you've got, check out the second column. The most recent thing in your application was a call to SubCategoryView numberOfSectionsInTableView:, in the fourth row (the one numbered '3'). The triggered exception was NSRangeException, where you attempted to retrieved object 4294967295 from an array with 22 objects in it. 4294967295 is how -1 looks if you cast a signed 32bit number to an unsigned number, so for some reason you're doing something like:
NSInteger variable = -1;
[array objectAtIndex:variable];
Somewhere within SubCategoryView's numberOfSectionsInTableView. Best immediate guess: some change between 4.1 and 4.2 is causing whatever method you use to populate the table to come up with the wrong results.
To advance from here, make sure you're doing a debug build and launch with command+y (or go to Build, Build and Run - Breakpoints On). When your program crashes this time the debugging window should appear, showing you exactly the line your program throws an exception at and allowing you to inspect the state of all your program variables at that point. The debugger will also allow you to place breakpoints, step through your program one line at a time and all the other usual things.

Dynamic Results and Covering Data

Today I have a question that sprouted off of this one: Database Results in Cocoa. It's regarding using the data that was returned by a database to create a certain number of questions. I am using a form of the following code (this was posted in the question).
NSMutableDictionary * interfaceElements = [[NSMutableDictionary alloc] init];
for (NSInteger i = 0; i < numberOfTextFields; ++i) {
//this is just to make a frame that's indented 10px
//and has 10px between it and the previous NSTextField (or window edge)
NSRect frame = NSMakeRect(10, (i*22 + (i+1)*10), 100, 22);
NSTextField * newField = [[NSTextField alloc] initWithFrame:frame];
//configure newField appropriately
[[myWindow contentView] addSubview:newField];
[interfaceElements setObject:newField forKey:#"someUniqueIdentifier"];
[newField release];
}
However, now when I attempt to use IFVerticallyExpandingTextfield (from http://www.cocoadev.com/index.pl?IFVerticallyExpandingTextField), or create any large amount of text, it simply goes over the other drawn content. I looked into using setAutosizingMask: on the object, but it has not worked so far.
Thanks for any help.
EDIT: What I want the effect to look like is called "Correct TextField" and what happens is called "StackOverflow Sample" - http://www.mediafire.com/?sharekey=bd476ea483deded875a4fc82078ae6c8e04e75f6e8ebb871.
EDIT 2: And if no one knows how to use this IFVerticallyExpandingTextfield class, would anyone know if there is another way to accomplish the effect?
Do you mean this?
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaDrawingGuide/GraphicsContexts/GraphicsContexts.html
Your question is not very clear to me but this might help ^^^.
Look at 'Modifying the Current Graphics State' on that page.
What about just exactly copying the code from the 'Correct textfield' example and use it in your application? Or start your application from the 'Correct texfield' example.
Also
A comment on http://www.cocoadev.com/index.pl?IFVerticallyExpandingTextField says:
Give it a try! You should be able to
throw this into a project, read the
files in Interface Builder, and use
the custom class pane to make an
NSTextField into an
IFVerticallyExpandingTextField. You'll
need to set the layout and
linebreaking attributes for word
wrapping for this to work.
Although expansion should work
properly when the textfield is
embedded in a subview, I'm having some
trouble dealing with NSScrollViews.
The field expands into the
scrollview's content view, but none of
the controls on the scrollbar appear.
Some help here would be appreciated.

Resources