In my iPhone application, I have the following line in a constructor:
self.myVar = myVar_in;
where myVar is a property and myVar_in is a parameter passed to the constructor.
When I run the code, I get an EXC_BAD_ACCESS error on this line. However, when I replace the line with:
[myVar release];
[myVar_in retain];
myVar = myVar_in;
the code runs fine. My property is declared like this:
NSNumber *myVar;
...
#property (retain) NSNumber *myVar;
The error is consistent and I'm positive it's not a variable scope issue. Can someone explain this behavior?
EDIT: I've confirmed that myVar_in is valid right before the line(s) are executed. Here's the actual code, although it won't help much:
-(GetAddressRequestHelper*)initWithRequest:(ClientRequest*)request delegate:(id<ServerResponseDelegate>)delegate number:(NSNumber*)myVar_in location:(CLLocation*)location {
self = [super initWithRequest:request delegate:delegate];
if( self ) {
// same behavior even if this line is uncommented!!!
myVar_in = [NSNumber numberWithInt:123];
// prints "myVar_in is 123"
NSLog(#"myVar_in is %#",myVar_in);
// doesn't throw exception
/*[myVar release];
[myVar_in retain];
myVar = myVar_in;*/
// throws exception
self.myVar = myVar_in;
self.location=location;
}
return self;
}
EDIT2: I've found I still get the behavior when I explicitly initialize the param with myVar_in = [NSNumber numberWithInt:123];!
Thanks
One critical difference between this code:
[myVar release];
[myVar_in retain];
myVar = myVar_in;
and this code:
self.myVar = myVar_in;
is the use of self to call the method (setMyVar).
Almost certainly your object has been incorrectly created/allocated and self is a random value (in which case the assignment of myVar = myVar_in is scribling over random memory).
Post the code showing your object creation/init call and for good how measure how myVar_in gets its value. Also post your init code (you can (very carefully) delete extraneous code, but since this is a weird case, any extraneous code might well be relevent...
Try using the auto-generated setter method if you have #property and #synthesize statements for your variable. That will make sure the value of myVar_in is retained when assigned to myVar ([self setMyVar:myVar_in])
The error that you see is probably because myVar_in is released by the time you use it.
Make sure myVar_in is actually initialized. Would you mind posting the code where you initialize myVar_in and call the initializer method?
Related
I am designing a new application by modernizing code I wrote in the past. This old code uses the class/delegate model and I am trying to transform them to use blocks as callbacks, not the delegate stuff.
What I do is to create a property like
#property (nonatomic, copy) void (^onTouch)(NSInteger index);
That would pass to the object using that class a block where code can be inserted and in this case executed on touch.
But my problem is this. When you use delegates and you have a method on the delegate protocol, Xcode will warn if you use that class and forget to implement the delegate protocols. Is that a way to do that with blocks? Or in other words: is there a way to make Xcode complain if a callback block is not defined by the caller?
I mean this would be the correct:
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = ^(NSInteger *index){ //call back code to be executed };
This would be OK too
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = nil;
but this would generate a message
MyClass *obj = [[MyClass alloc] init];
// no callback block defined.
Is this possible?
If you want to enforce setting a certain parameter, I would include it in the initializer.
MyClass *obj = [[MyClass alloc] initWithBlock:^(NSInteger *index) { /* code*/ }];
Then, in MyClass:
- (id)init {
// This will result in a runtime error if you use the wrong initializer.
NSAssert(NO, #"Use initWithBlock instead.");
}
- (id)initWithBlock(initWithBlock:^(NSInteger *)block) {
self = [super init];
if (self) {
self.onTouch = block;
}
return self;
}
Also note, attempting to execute a NULL block results in a crash, so make sure to do:
if (self.onTouch) { self.onTouch(); }
Wherever you run the block.
First, I strongly recommend defining types to represent your blocks - makes them a lot easier to work with, especially if you need to refactor the parameters.
You can't write code that distinguishes between "I set this property to nil" or "the runtime initialized this property to nil", at least not without some crazy runtime code to check the stack. Only option I can think of would be to use the null object pattern. Before I elaborate, bear in mind that I haven't actually tried to test this, but it should work. Define a block that means 'has no value' and set your property to point to that block on init. Then you can compare to that NullBlock at runtime to identify if someone explicitly set the property to nil (because it would be nil at that point) or gave it a real non-nil value.
Alternatively, if you don't mind manually writing your set accessors, you could have a BOOL that tracks if someone set the property explicitly. Then when you call the block just check if someone actually set the value or not.
#synthesize onTouchBlock=_onTouchBlock;
MyBlock _onTouchBlock;
BOOL _onTouchBlockWasSet;
- (void)setOnTouchBlock:(MyBlock)block {
_onTouchBlockWasSet = YES;
_onTouchBlock = block;
}
I would not recommend passing the value in the initializer because that makes it tied to the creation of that object type. If you wanted to change the block in code based on some condition, you'd be back to square one. Also, it prevents you from using storyboards which create that object.
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?
.h
# interface MyClass : NSObject {
UILabel *mTextLabel;
}
#property (nonatomic, retain) UILabel *mTextLabel;
and Declare #synthesize mTextLabel in the MyClass.m;
and release the object like this.
[self setMTextLabel:nil];
[mTextLabel release];
NSLog (#"%d",[mTextLabel retainCount]);
This result is 0. and I have not found any error or interrupt.
But. When I release mTextLabel like this. I have just got EXC_BAD_ACCESS
[mTextLabel release];
[self setMTextLabel:nil];
I don't understand why it happen. Plz help me.
When you have a synthesized property with the retain attribute, the synthesized setter calls release on the old ivar before it sets the new value.
Here is an expanded view of what is happening in the first example:
[mTextLabel release];
mTextLabel = nil;
[mTextLabel release];
Since calling a method on a nil pointer does nothing, there is no problem.
In the second example, here is what is happening:
[mTextLabel release];
[mTextLabel release];
mTextLabel = nil;
See the problem?
Edit: it is also worth noting that inspecting the retain count of an object is rarely useful, as any number of Cocoa classes may retain it for their own purposes. You just need to be sure that every time you call retain, alloc, copy or new on an object, there is a matching release or autorelease somewhere in your code.
The problem is you are calling release then you are setting the property to nil which also sends a release to mTextLabel before setting it to nil. This is what happens when the property is defined as copy or retain. All you need is the following code.
[mTextLabel release];
mTextLabel = nil;
Edit:
I would like to add that in your code outside of init and dealloc it is completely fine to call self.mTextLabel = nil to properly release if necessary and nil the value of the property. It is however recommended to NOT use the property in the init/dealloc calls. In those cases you will want to create / release the objects directly to avoid the side effects of the accessor.
The value is already released when you do [self setMTextLabel:nil]. You don't need to release the value explicitly (unless you created the value using an init or copy method, in which case you should release it as soon as you've assigned to self.mTextLabel).
Note that retainCount has a return type of NSUInteger, so cannot ever be negative. So checking to make sure the retain count is zero and not -1 doesn't work.
For the following two lines in an obj-c class:
self.instanceVar = X
instanceVar = X
Is the only difference that the 'self' version calls the synthesized 'setter', whereas the latter simply sets the instanceVar and doesn't go through the setter?
Thanks
Yes. The implication of this is that the synthesized getter will wrap additional code depending on how the property is specified - so use of assign / retain / copy along with nonatomic / atomic change the behaviour.
Imagine the following:
#property( retain ) NSString * myprop;
If you set it by self.myprop, the NSString instance will be retained.
If you set directly the instance variable, this will not be the case.
So always use the self., unless you're absolutely sure...
This is an excellent question and understanding the difference between setting a variable through its accessor rather than directly assigning it is very important.
Here's what happens: when you declare a #property (nonatomic, retain) NSString *variable in the header, you add a property to your object. Simple enough. Calling #synthesize does the following thing: it generates two methods in your class, setVariable: and getVariable. Of course, if you name your property "name", the methods will be setName: and getName.
Now, it is important for you to understand what happens in the setVariable: method. The method is declared something like this:
- (void)setVariable:(NSString *)theVariable {
if (variable != nil) {
[variable release];
}
// variable is the class member,
// theVariable is the object that was sent by the method parameter
variable = [theVariable retain];
}
When you call self.variable = #"test"; you will actually call [self setVariable:#"test"] which is exactly the method that was generated by the #synthesize call!
When you call variable = #"test"; you do just that - you assign a string to a variable, without retaining it or anything.
If you were to call self.variable = nil the current value of the variable would be released and variable will be assigned to nil, but if you were to call variable = nil you would just ditch the reference to the previously assigned value (object). Therefore, if you would be calling
self.variable = #"test";
// wrong, do not do this in this case
variable = nil;
you would be be generating a memory leak because the #"test" object that was assigned to variable and retained through its accessor is never going to be released. Why's that? Because the setter (setVariable:) never gets called to know to release the previously held value.
For the sake of example, here's what getVariable looks like:
- (void)getVariable {
// variable is the class member
return variable;
}
Let me know if you have further questions.
Yes. self.instanceVar accesses the value through the property.
Although it is not necessarily the synthesized property. You can write your own get and set methods that can be called.
I having trouble getting a variable assignment to work in the following initializer:
- (id)initWithBaseURL:(NSString *)url {
self = [super initWithNibName:#"MyNibName" bundle:nil];
if (self) {
baseURL = [url copy];
}
return self;
}
I have verified that the url parameter is valid with appropriate content (created as a NSMutableString built via an NSXMLParser, then examined in the initWithBaseURL method in the debugger), but after assigning the result of the copy operation to baseURL, the baseURL variable is "out of scope" and remains invalid. Any attempts to access the baseURL variable in other methods of the class result in an EXC_BAD_ACCESS error.
I have declared baseURL in the .h file as follows, with no #property or #synthesize operations, since I'm trying to use it as a private member variable:
#interface SignInViewController : UIViewController {
// other variables
#private
NSString *baseURL;
}
// Other #property delcarations, IBAction method declartions, and method declarations
#end
I've also attempted to use the technique described here, but that only causes the EXC_BAD_ACCESS error to occur in the initializer.
What am I doing that would cause the EXC_BAD_ACCESS errors to occur, and how do I fix it?
Here's a couple of observations which may or may not be relevant.
The code you posted is absolutely OK from Obj-C's point of view.
Instead of [super init] you should really call UIViewController's designated initializer, initWithNibName:bundle:.
You named the method initWithBaseURL:, but it takes NSString as an argument. While that does match the declaration in #interface, make sure you don't expect baseURL to be an NSURL object somewhere else in your code.
What happens when you remove #private? I suspect nothing changes with regard to the exception you see.
Make sure you don't do this:
NSString *myURL = [NSString stringWithFormat:...]; // myURL is autoreleased
SignInViewController *controller = [[SignInViewController alloc] initWithBaseURL: myURL]; // retains myURL because it's immutable
[myURL release]; // does not crash because myURL has been *retained*
// baseURL is left with 0 retain count
You can verify that sending copy to an NSMutableString does produce another object, while sending copy to an immutable NSString is equivalent to retain, just because wasting memory on exact copies of immutable objects is inefficient. If baseURL were indeed a copy of myURL, the crash would happen when the autorelease pool was drained.
In other words, a mistake may be in one place, only to manifest itself in another. The above example is not too contrived.
you've declared it correctly. also, the program you have posted is correct.
corr:
as Costique caught: you should call through one of the superclass's designated initializers.