iphone ARC - how to set an object to nil? - objective-c

I have some logic that test if an object is nil or not, how can I set it to nil?
Like:
// in some method
if (true){
[self myObj] = [[myObj alloc]init];
} else{
[self myObject] = nil; //??? How to do this with Automatic Ref. Counting
}
// elsewhere
if([self myObj]){
}

Your code is incorrect.
You need to use properties and assign values to them, like [self setMyObject:nil]; or [self setMyObj:[[myObj alloc] init]];.

Your code is wrong. Try:
self.myObject=nil;
//or
[self setMyObject:nil];
Also, make sure that myObject is a property in your class, otherwise using self won't work.

[self myObj] is not assignable, as it is not a lvalue. To fix this, either reference the underlying variable, e.g. self->myObj, or use [self setMyObj:] if you are using properties.

You are using a getter as a setter. That isn't going to work. It should be
[self setMyObj:[myObj alloc]init]];
And
[self setMyObj:nil];
Assuming you have implemented the setters. Under ARC you don't really need to if you are just accessing an ivar - you can access it directly and the reference counting will be done for you:
myObj = [MyObj alloc] init];
And
myObj = nil;
Will set and remove everything for you.

Related

How to pass ivar into a function and set it without losing the reference to the original object

I am passing an ivar (NSMutableArray) into some method. I was expecting that if I modify the object inside the function, it would be reflected outside the function, but in this case I need to set the object; something like the following:
- (void) someMethod:(SMResponseObject *)response onData:(NSMutableArray *)imAnIvar {
imAnIvar = [response objects];
//Some other stuff
}
But I noticed that the memory reference of imAnIvar inside the function changes when I set it, and given that, the actual ivar doesn't change. I understand that the problem is that I'm changing the reference of the object inside the method, so it stops pointing to the ivar and then it points to some other random memory direction.
I thought about one solution to this problem, and it can be to ensure that the ivar is not nil before calling the function and do something like this:
- (void) someMethod:(SMResponseObject *)response onData:(NSMutableArray *)imAnIvar {
NSMutableArray *data = [response objects];
[arrayForTableView removeAllObjects];
for(id element in data){
[imAnIvar addObject:element];
}
//Some other stuff
}
So I use the original object instead of setting it directly. The problem is that in order for this to work I need to ensure that the ivar is not nil, which I think is not clean, because I'll need to do something like this on every call to the method:
if(!_ivar){
//alloc it
}
So my question is: Is there a way to force the local scope variable to point to the original variable even if I'm setting it? if not, is there any cleaner way to make this work?
Do you mean this?
- (void)setFoo:(SomeClass **)objPtr
{
*objPtr = someOtherObject;
}
// call it as:
SomeClass *foo = someObject;
NSLog(#"Before: %#", foo);
[self setFoo:&foo];
NSLog(#"After: %#", foo);
Why not use a getter for the array so that you need not check for the array being nil while using it?
-(NSMutableArray *)iAmAnIvar {
if(_iAmAnIvar == nil) {
_iAmAnIvar = [NSMutableArray array];
}
return _iAmAnIvar;
}
And when you have to set a value to the array, as you mentioned in your question, you could use
[self.iAmAnIvar removeAllObjects];
[self.iAmAnIvar addObject:someObj];
I believe you can use the - (id)copy; function of NSObject
so your code might look like this:
- (void)someFunction:(NSString *)someArg
{
NSString *str = [someArg copy];
}

Initializing object in if condition

I want to create a object but the type depends on the result of the if-condition:
if ([type isEqualToString:#"day"]) {
GraphDayView *graphv = [[GraphDayView alloc] initWithFrame:rect];
} else {
GraphMonthView *graphv = [[GraphMonthView alloc] initWithFrame:rect];
}
The problem is that graphv is out of scope, so I am not able to use it after the if-statement.
So I tried to declare it as an id:
id graphv;
if ([type isEqualToString:#"day"]) {
graphv = [[GraphDayView alloc] initWithFrame:rect];
} else {
graphv = [[GraphMonthView alloc] initWithFrame:rect];
}
But the problem now is, that the compiler doesn't know what kind of object grapv is. So:
graphv.backgroundColor = [UIColor whiteColor];
gives an error. Anyone an idea how to solve this one?
If they share the same superclass, then use that instead of id. Otherwise, create two variables and set them to nil:
GraphDayView *gdv = nil;
GraphMonthView *gmv = nil;
Then test after the if statement to see which one was initialized.
Make GraphDayView and GraphMonthView both subclasses of GraphCalendarView. Then make your backgroundColor a property of GraphCalendarView.
(Or, if your two classes are already subclasses of a UI class that implements backgroundColor then you're home free.)
Declare your variable as GraphCalendarView graphv;, and then proceed as before. If you need to use a property/method that's unique to of one or the other of your two derived classes then cast to that class first.
Try this,
id graphv;
if ([type isEqualoString:#"day"]) {
(GraphDayView *)graphv = [[GraphDayView alloc] initWithFrame:rect];
} else {
(GraphMonthView*)graphv = [[GraphMonthView alloc] initWithFrame:rect];
}
A lot of suggestions for a common superclass have been made which are most liekly suitable.
In the event they are not then you could use typcasts. However, typecasts may cause exceptions like unknown selecor or bad_exec. If you want to savely use typcasts then you should always check either isKindOfClass or respondsToSelector.
Sample:
id someClassObject; //or any other common superclass* instead of id
If ([something isTrue])
someClassObject = [[AClass alloc] init]; //assuming ARC. If not then you may want to retain/autorelease here too.
else
someClassObject = [[AClass alloc] init];
... //some code
if ([someClassObject isKindOfClass:[AClass class]])
[(AClass *) someClassObject methodOfAClass];
//
if ([someClassObject isKindOfClass:[AClass class]]) {
AClass *aClassTemp = (AClass *) someClassObject;
[aClassTemp methodOfAClass];
aClassTemp.propertyOfAClass = someValue;
}
if ([someClassObject respondsToSelector:#selector(methodOfBClass:))
[someClassObject perforformSelector:#selector(methodOfBClass:) withObject:[UIColor clearColor]];
Note the number of : following the selector name correlates to the number of parameters of the method. You may not find a suitable variance of performSelector for each possible method call. Especially as you can pass or return references to objects only.
Again, both of them I would only suggest if the common subclass is not suitable in your case.

Calling [self methodName] from inside a block?

I've just run into blocks and I think they are just what I'm looking for, except for one thing: is it possible to call a method [self methodName] from within a block?
This is what I'm trying to do:
-(void)someFunction{
Fader* fader = [[Fader alloc]init];
void (^tempFunction)(void) = ^ {
[self changeWindow:game];
//changeWindow function is located in superclass
};
[fader setFunction:tempFunction];
}
I've been searching for a couple of days and I can't find any evidence that this is possible.
Is this at all possible, or am I trying to use blocks for something they aren't meant for?
The reason I'm using blocks is that I've created a Fader class, and I want to store a block for it to execute when it finishes fading out.
Thank you
EDIT:
Okay, I added in the suggestion, but I'm still getting an EXC_BAD_ACCESS error...
-(void)someFunction{
Fader* fader = [[Fader alloc]init];
__block MyScreen* me = self;
void (^tempFunction)(void) = ^ {
[me changeWindow:game];
//changeWindow function is located in superclass
};
[fader setFunction:tempFunction];
[fader release];
}
Maybe I'm not allowed to give fader the function...?
Yes, you can do this.
Note, however, that the block will retain self. If you end up storing this block in an ivar, you could easily create a retain cycle, which means neither would ever get deallocated.
To get around this, you can do:
- (void) someMethodWithAParameter:(id)aParameter {
__block MySelfType *blocksafeSelf = self;
void (^tempFunction)(void) = ^ {
[blocksafeSelf changeWindow:game];
};
[self doSomethingWithBlock:tempFunction];
}
The __block keyword means (among other things) that the referenced object will not be retained.
The accepted answer is outdated. Using __block in that case can cause errors!
To avoid this problem, it’s best practice to capture a weak reference to self, like this:
- (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // capture the weak reference
// to avoid the reference cycle
}
}
Please, look at Apple Documentation - Avoid Strong Reference Cycles when Capturing self
for more details.
__block CURRENTViewController *blocksafeSelf = self;
[homeHelper setRestAsCheckIn:strRestId :^(NSObject *temp) {
[blocksafeSelf YOURMETHOD:params];
}];
Is it possible to call a method [self methodName] from within a block?
Yes, why not. If your tempFunction is an instance method, you can do it. The called method should be accessible is the only restriction.
Consider this (which I think is the best practice)
#implementaion ViewController
- (void) viewDidLoad {
__weak typeof(self) wself = self;
[xxx doSomethingUsingBlock: ^{
__strong typeof(wself) self = wself;
[self anotherMessage];
}];
}
#end
Moreover, You can define wrapper macros.
#define MakeWeakSelf __weak typeof(self) wself = self
#define MakeStrongSelf __strong typeof(wself) self = wself
I wonder whether you [fader setFunction:tempFunction]; then is synchronous or asynchronous.
blocks push onto stack.so in MRR,if you don't retain it,it will pop off.
-(void)someFunction{
Fader* fader = [[Fader alloc]init];
void (^tempFunction)(void) = ^ {
[self changeWindow:game];
//changeWindow function is located in superclass
};
[fader setFunction:tempFunction];
//if the tempFunction execute there will be right.
}//there the tempFunction pop off
//....some thing go on
//execute the tempFunction will go wrong.

What would be a proper way to initialize a instance variable in Objective C without leaking memory?

I have a class like this:
#interface MyCollection : NSObject {
NSMutableDictionary *data;
}
and in the implementation of it, I have a method to init it like this:
- (id) init {
if(self = [super init])
{
self.data = [[NSMutableDictionary alloc] init];
}
return self;
}
Now when I create a object of this class in my code like this:
MyCollection *c = [[MyCollection alloc] init];
... at which point the Leaks utility shows that I have a memory leak in the init function on the very line where I try to set up the instance variable. I am totally new to Objective C & Iphone and I can't just get what is going wrong here. I have read through the Memory Management Guide and all, but I think I'm missing something pretty serious here.
Any help would be greatly appreciated. Thanks for your time already.
you are using self.data =. So there is most likely a property. And it most likely is a property which either copies or retains your object if you use it.
By calling
self.data = [[NSMutableDictionary alloc] init];
The retain count of the NSMutableDictionary increases because of the alloc, and if the property of data has a retain or copy statement you get another increase in retain count.
you could write data = [[NSMutableDictionary alloc] init]; or self.data = [NSMutableDictionary dictionary]. This would increase the retain count only one time.
And don't forget to release the object in dealloc.
You have to release the object in your dealloc method. That's why it's showing up as a leak.
to add to what fluchtpunkt mentioned you could try this instead:
- (id) init {
if(self = [super init])
{
self.data = [NSMutableDictionary dictionaryWithCapacity:0];
}
return self;
}
and in the dealloc
-(void)dealloc
{
self.data = nil;
}
I see weird situations with the Leaks utility as sometimes it reports old leaks, sometimes it doesn't report new ones, and so on. Also, from what I could collect with all your answers and opinion elsewhere on the web, people are divided on whether one should set a pointer to nil or not.
As of now, I have solved the situation with the following approach.
- (id) init {
if(self = [super init])
{
data = [[[NSMutableDictionary alloc] initWithCapacity:0];
}
return self;
}
-(void)dealloc
{
[data release];
}
Thanks everyone for contributing.
Are you creating the instance of "MyCollection" in the interface section?
If it has method scope try to release it in the same method after you are done with it.

How to release objects stored in an array?

Please look at the code below and suggest the best approach. I can't quite tell whether the code is correct. When adding objects to arrays, do they get a retain count? In the second function, am I releasing the local variable "mySubview" or the original object?
// this is a class property
myArray = [[NSMutableArray alloc] init];
- (void)createSubview
{
UIView *mySubview = [[UIView alloc] init];
[self addSubview:mySubview];
[myArray addObject:mySubview];
}
-(void)eventHandler:(NSNotification *) notification
{
UIView *mySubview = [notification object];
[myArray removeObjectIdenticalTo:mySubview];
[mySubview removeFromSuperview];
[mySubview release];
}
When adding objects to arrays, do they
get a retain count?
Yes.
In the second function, am I releasing
the local variable "mySubview" or the
original object?
UIView *mySubview;' defines a local variable, mySubview, which is a pointer to -- a reference to -- an instance of the UIView class. There is no such thing as a "local object" or "stack object" in Objective-C (save for blocks, but that is beyond the scope of this question).
So, no, when you call [mySubview release] you are sending -release to the instance of of UIView included in notification.
That release is balancing the retain implied by the alloc. Which isn't the right pattern at all. You should do something like:
- (void)createSubview
{
UIView *mySubview = [[UIView alloc] init];
[self addSubview:mySubview];
[myArray addObject:mySubview];
[mySubview release];
}
-(void)eventHandler:(NSNotification *) notification
{
UIView *mySubview = [notification object];
[myArray removeObjectIdenticalTo:mySubview];
[mySubview removeFromSuperview];
}
Oh, by "class property", I'm assuming you mean "instance variable"?