I need to call a function after a CCSpeed delay so that I can change the speed dynamically. As far as I know, I can't use a CCSpeed inside a CCSequence, so I tried using a CCSequence (CCDelayTime, CCCallFuncND, nil) inside a CCSpeed, which also doesn't appear to work. Anyone have any suggestions?
In my BackgroundLayer class, I have a NSNumber *multiplierSpeed; :
#interface BackgroundLayer : CCLayer {
NSNumber *multiplierSpeed;
}
#property (nonatomic, retain) NSNumber *multiplierSpeed;
-(void)rotateWorldAndSwapIn:(id)sender data:(int)frame;
#end
And in my implementation:
#synthesize multiplierSpeed;
-(id)init {
self = [super init];
if (self != nil) {
//...
multiplierSpeed = [NSNumber numberWithFloat:1.0f];
CCSpeed *delay = [CCSpeed actionWithAction:[CCSequence actions:[CCDelayTime actionWithDuration:20],[CCCallFuncND actionWithTarget:self selector:#selector(rotateWorldAndSwapIn:data:) data:(void*)3], nil] speed:1.0f];
[delay setTag:10];
[self runAction:delay];
//...
}
}
-(void)rotateWorldAndSwapIn:(id)sender data:(int)frame {
CCLOG(#"test"); //This is fine
CCLOG(#"multiplierSpeed=%f",[multiplierSpeed floatValue]); //Crashes here
//...
}
I am getting this error:
EXC_BAD_ACCESS on callbackMethod_(targetCallback_,selector_,target_, data_);
in the CCActionInstant.m file.
Also, is it because CCSpeed wraps around CCCallFuncND, which has no delay, that's throwing this error? If so, do you have any alternative suggestions?
Thanks for helping!
You need to use self.multiplierSpeed instead of only multiplierSpeed.
self.multiplierSpeed = [NSNumber numberWithFloat:1.0f];
and then in the CCLOG,
CCLOG(#"multiplierSpeed=%f",[self.multiplierSpeed floatValue]); //Won't crash anymore
Unless you use self.varName, Objective-c won't be using the getter/setter methods, rather would try to access the instance variable directly causing a bad memory access (as no memory was allocated for that var)
It's crashing because this:
multiplierSpeed = [NSNumber numberWithFloat:1.0f];
...isn't retained in your init. By the time you get to the action callback, that object has been released and is invalid. There's probably no good reason to store that value in an NSNumber anyway. Set up your iVar as a CGFloat and then you can store and access the multiplier without worrying about retains. If you really need that as an NSNumber, you'll need to retain it in init (or through a property) and then release it when appropriate, at least in your dealloc.
Related
Wow. I have had a total mental failure this morning stuck on this 101 problem.
In ViewController, I have this code. But after it executes, the value of [proposalInfo expanded] is still NO. Can somebody see what I'm doing wrong?
- (void)showFullProposal:(id) sender {
// update proposalinfo
ProposalInfo *proposalInfo = [self.proposalInfoArray objectAtIndex:index.section];
[proposalInfo setExpanded:YES];
}
The variables are declared as follows:
ViewController:
#interface ViewController()
#property (nonatomic, strong) NSMutableArray* proposalInfoArray;
#end
ProposalInfo.h:
#interface ProposalInfo : NSObject
#property (assign) BOOL expanded;
#end
ProposalInfo.m:
#synthesize expanded;
Please help!!
If you never alloc/init your proposalInfoArray array, you could experience behavior like this (i.e. get no error, but always get NO back because when you send a message to a nil object, you get nil back). If not precisely this, it's going to be something simple like that. Check proposalInfoArray and make sure it's not nil. Also check the proposalInfo object you got back, make sure it's not nil.
To illustrate your likely problem, this reproduces the behavior you describe (e.g. expanded looks like it's NO, regardless, but you still don't get any exception):
self.proposalInfoArray = nil; // This obviously won't work
[self.proposalInfoArray addObject:[[ProposalInfo alloc] init]];
ProposalInfo *proposalInfo = [self.proposalInfoArray objectAtIndex:0];
NSLog(#"before=%d", proposalInfo.expanded); // OK, IT'S "0"
proposalInfo.expanded = YES;
NSLog(#"after=%d", proposalInfo.expanded); // HEY, IT'S STILL "0" -- BAD!
Whereas this works properly:
self.proposalInfoArray = [[NSMutableArray alloc] init];
[self.proposalInfoArray addObject:[[ProposalInfo alloc] init]];
ProposalInfo * proposalInfo = [self.proposalInfoArray objectAtIndex:0];
NSLog(#"before=%d", proposalInfo.expanded); // OK, IT'S "0"
proposalInfo.expanded = YES;
NSLog(#"after=%d", proposalInfo.expanded); // HEY, IT'S NOW "1" -- GOOD!
In terms of how to identify these issues in the future, use NSAssert. We would have found this problem if we had the following line of code before the objectAtIndex line:
NSAssert(self.proposalInfoArray, #"proposalInfoArray must be initialized!");
or, after the objectForIndex:
NSAssert(proposalInfo, #"proposalInfo must not be nil!");
The nice thing about NSAssert statements is that you can put them in your code, and when you build for debugging, they help you find your program logic mistakes, but when you build your final release version, they're automatically omitted, making your code more efficient. So, use NSAssert liberally!
Imme, the following line seems to be strange:
ProposalInfo *proposalInfo = [self.proposalInfoArray objectAtIndex:index.section];
Actually, what do you have in your array, means in proposalInfoArray.
Have you checked your object?
I've been sitting on this error for hours now. I'm getting an EXC_BAD_ACCESS (code=2) on the line:
[self.downloadQueue addOperation:self.downloadOP];
I know it has to be related to memory conflicts, but I just can't find the problem. The class that manages the OperationQueues is a singleton, but i think thats not the problem.
Here's the shortened version of my .h file:
#interface GTMConnectionManager : NSObject{
}
#property (retain) GTMDownloadOperation *downloadOP;
#property (retain) NSOperationQueue *downloadQueue;
// it doesn't make a difference if I add 'nonatomic' to these properties
+ (GTMConnectionManager *)sharedConnectionManager;
-(void)downloadImageData:(NSMutableArray*)p_images andController:(UIViewController*)p_resultsController;
#end
And the important part of the .m file:
#import "GTMConnectionManager.h"
#implementation GTMConnectionManager
#synthesize downloadOP, downloadQueue;
+ (GTMConnectionManager *)sharedConnectionManager
{
static GTMConnectionManager * instance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
instance = [[super allocWithZone:nil] init];
});
return instance;
}
-(void)downloadImageData:(NSMutableArray*)p_images andController:(GTMResultsListViewController*)p_resultsController{
self.resultsController = p_resultsController;
[self.downloadQueue setMaxConcurrentOperationCount:2];
self.downloadQueue = [[[NSOperationQueue alloc]init]autorelease];
// it doesn't make a difference if I do this with or without 'autorelease'
for (int i = 0; i < [p_images count]; i++) {
GTMGeoImage *tmpImg = [p_images objectAtIndex:i];
self.downloadOP = [[[GTMDownloadOperation alloc]initWithImage:tmpImg]autorelease];
[self.downloadQueue addOperation:self.downloadOP]; //Here's the error
}
}
When I add a breakpoint just before the error-line, both self.downloadQueue and self.downloadOP are retained correctly (not nil).
The strange this is: in this very class I have a second NSOperationQueue with other NSOperations that are declared and treated in the same way as downloadQueue and downloadOP. And they work perfectly.
And yes, GTMDownloadOperation is a child-class of NSOperation and has a -(void)main method.
I don't know what to do now. If you don't have any idea about the reason of that error, how can I analyze the situation more accurately? (Product > Analyze doesn't complain about potential leak at that position).
Thanks for your help.
Oh man...
That took a while, but finally I know that the problem was in the for-loop.
The statement
self.downloadOP = [[[GTMDownloadOperation alloc]initWithImage:tmpImg]autorelease];
was accessing the variable downloadOP again and again in every iteration. Using the same NSOperation seemed to crash its retainCount.
I changed it to
GTMDownloadOperation *downloadOP = [[GTMDownloadOperation alloc]initWithImage:tmpImg];
and it works without error. Silly me.
you're not calling [super init] inside your constructor?
Assuming you're subclassing NSOperation (or NSObject etc...), you probably should!
why do you realloc and init your queue?
couldn't it be just one for your singleton class?
and...
[self.downloadQueue setMaxConcurrentOperationCount:2];
self.downloadQueue = [[[NSOperationQueue alloc]init]autorelease];
the first line is executed on an old queue, then you create a new one that have no limits
when do you call the second line (alloc a new queue) you release the old one (if any)
try this:
if (!downloadQueue){
self.downloadQueue = [[[NSOperationQueue alloc]init]autorelease];
}
[self.downloadQueue setMaxConcurrentOperationCount:2];
and remember to add:
self.downloadQueue = nil;
in your dealloc method (even if it's a singleton, and it won't be called while your app is running)
I believe the problem is in the implementation of your operation. I suggest two courses: try creating some simple block based operations that just log hello world and add them to the queue. They will most likely wok, letting you know the queue is functional. Then start adding log messages to you subclass to see which methods get called and finish properly.
This should lead you to the problem.
I have a setter like this:
- (UIImagePickerController *) foto {
if (_foto == nil) {
_foto = [[UIImagePickerController alloc] init];
_foto.delegate = self;
}
return _foto;
}
it is declared like
#property (nonatomic, retain) UIImagePickerController *foto;
with
#synthesize foto = _foto;
on my dealloc I have
[_foto release];
At some point in my code I want to do this
self.foto = nil;
but something in my soul says the object assigned to self.foto previously will leak, because it was alloc on the setter... how do I make it right?
thanks.
Edit: No, that should be fine. As long as you don't assign something else to _foto before you release, it should work.
Yup. You create an object, then loose the pointer to it. If you throw an autorelease on the init line, that will fix it. You could also use ARC.
The init line doesn't actually do anything... You assign the pointer to an object you create, then assign it to something else.
I don't think there is a leak there. When you assign to self.foto like this:self.foto = nil;, it will release the former one automatically. If you assign it by this way: _foto = nil;, you need to release it manually before the assignment.
Yes that works, and will not leak. When you set the value of _foto, its retain count is 1 (because you called alloc). As long as you release it (which you've said you do) in dealloc, you should be fine, as the retain count will go back to 0. UNLESS your setter is ALSO written by you, and written improperly. It needs to explicitly release the old value, if it's not nil. Something like this:
- (void)setFoto:(UIImagePickerController*)foto {
if (_foto) {
[_foto release];
_foto = nil;
}
if (foto)
_foto = [foto retain];
}
i wrote this code :
bs = [NSNumber numberWithFloat:[mystring.text floatValue]];
NSLog(#"bs = %#",bs);
....
float x = [bs floatValue];
when the program want to excute the line above it crash why ?
the output :
bs = 2.8 which true 100%
Between the time you assigned an NSNumber object to your ivar bs it was release by the runtime. I am assuming where you created bs and where you try to assign it to x are in two different methods. If that is the case, you need to tell the runtime you want to keep the ivar bs around for awhile:
[bs retain];
And if you do that, you need to tell the runtime you are done in the dealloc:
-(void)dealloc {
[bs release];
[super dealloc];
}
Basically, if you didn't create an object with alloc, copy, mutableCopy, new in the method name, then you don't own the object.
the code snippet seems ok.
and I guess the reason is bs was released before fetching its float value.
the simplest way to retain bs is to change it to a property:
#property (nonatomic,retain)NSNumber* bs;
and release it in dealloc
for a day now I stare at the following routine and can't get my grips around it. I have a class such as:
MyClass.h
...
#interface MyClass : NSObject {
NSNumber *myNumber1;
NSNumber *myNumber2;
}
#property (nonatomic, retain) NSNumber *myNumber1;
#property (nonatomic, retain) NSNumber *myNumber2;
#end
and the m-file
#import "MyClass.h"
#synthesize myNumber1, myNumber2;
#implementation MyClass
-(id) init {
self = [super init];
NSLog(#"Retain Counts myNumber1: %d, myNumber2: %d", [myNumber1 retainCount], [myNumber2 retainCount]);
myNumber1 = [NSNumber inbitWithint:10];
myNumber2 = [NSNumber inbitWithint:2];
NSLog(#"Retain Counts myNumber1: %d, myNumber2: %d", [myNumber1 retainCount], [myNumber2 retainCount]);
return self;
}
...
I use this class as a globals container and instantiate it from every other class in my app using a
MyClass *myGlobals = [[MyClass alloc] init];
===>>> the weirdo >>>
Running the routine I have the following facinating console output:
Retain Counts (before) - myNumber1: 0, myNumber2: 0
Retain Counts (after) - myNumber1: 1, myNumber2: 26
How can that be?
Do not call `retainCount`
The return value of retainCount can only be considered accurate if you are calling it on an instance of a class you wrote and you have never ever not even once passed said instance to any API provided by the system.
The absolute retain count is an implementation detail for which you might not have any control.
Assuming, for the moment, that your code was typed and, thus, the errors are not really in your original code...
NSNumber caches a subset of common values. That is, for some numeric values, there is a single instance of NSNumber that is returned for all requests to retrieve an NSNumber instance wrapping that number.
If this is your actual code, it shouldn't work at all, it should simply crash. If not, you should cut and paste your actual code.
However, a couple of things.
First, if you use an NSNumber as an ivar, like anything else, you must take ownership of it. If you plan to use a convenience constructor, you must either assign it using the property on self, or send it an explicit retain message.
Second, you typed something like initWithInt: here. If you are actually using that message, then you have never actually allocated the number in the first place. You must replace this with one of the following:
myNumber1 = [[NSNumber alloc] initWithInt:10]; // explicitly create the number
myNumber1 = [[NSNumber numberWithInt:10] retain]; // take ownership through retain
[self setMyNumber1:[NSNumber numberWithInt:10]]; // use the property accessors to deal with ownership and the convenience constructor to create the number
However you choose to do it, you must at some point take ownership of the object.
You should never even look at retainCount as it carries little significance anyway. The reason you see the count of 26 is that probably somewhere in the frameworks (or even in your own app) other instances of NSNumber exist that are created with the same int 2. An NSNumber is immutable, and so as an optimization NSNumber probably just gives you back an instance it has created earlier. As long as you don't look at retainCount it doesn't matter to you.