#property, #synthesize and releasing objects in Objective-C - objective-c

I developing iPad games. I encounter this thing. This is my sample code:
Method 1:
Foo.h
#interface Foo : UIView{
UILabel *title;
.... // Other objects like UISlider, UIbuttons, etc.
}
// I add #property for all the objects.
#property (nonatomic, retain) UILabel *title;
... blablabla
Foo.m
// I synthesize all the properties.
#synthesize title;
... blablabla
// Release in dealloc method
[title release];
....
[super dealloc];
Method 2:
Foo.h
#interface Foo : UIView{
UILabel *title;
.... // Others object like UISlider, UIbuttons, etc.
}
// But this time I didn't add #property, synthesize and release.
Foo.m
// When I need the label, I allocate it:
title = [[UILabel alloc] initWithRect: CGRect(10, 10, 100, 30)];
title.text = #"test";
[self addSubview: title];
[title release];
Both method 1 and 2 work, but what's the difference between the 2 methods (method2 has less code)?
Which method should I use and why?
Has it something to do with memory management?

Method 2 is technically incorrect since by sending -release to title you indicate that you are no longer interested in it. You should make it nil straight after, or better yet, make it a local variable.
Method 1 is absolutely fine and has the advantage that, outside of -dealloc provided you always use the property to refer to it, you do not need to worry about getting -retain and -release -right.

The difference is that, in Method 2, you won't have access to the title from outside the Foo object. Instance variable are private to the class.
Also, with you need to make sure you balance the alloc/retain and releases.

Related

how to run some code when one object dealloc

There is a need to run some code when one object get dealloc. E.g, I set up one observer which updates the label A's text when object B's name changed. When label A's retain count reach 0, I want to remove the observer from B.
Possible solutions:
1 Subclass and call the clean code in dealloc.
2 Create a wrapper class which able to run arbitrary code in dealloc and associate this object to label A. When A get dealloc, the associated object get dealloc too (suppose only A holds the strong ref to it), then the code get called.
I don't like the 1st one since it is so intrusive that makes it barely useless, need to subclass just for some easy stuff. So I am using No.2.
Do you have any comments? How do you do it?
I put my solution here in case someone needed.
#interface ExecuteWrapper : NSObject
#property (nonatomic, copy) void(^block)();
-(void)dealloc;
#end
#implementation ExecuteWrapper
-(void)dealloc{
if(self.block){
self.block();
}
}
#end
#implementation NSObject (SLUtil)
+(void)executeWhenDealloc:(NSObject *)object block:(void(^)())block{
static char key;
NSMutableArray *executeWrapperArray = [object associatedValueForKey:&key];
if ( executeWrapperArray == nil){
executeWrapperArray = [NSMutableArray array];
[object associateValue:executeWrapperArray withKey:&key];
}
ExecuteWrapper *executeWrapper = [[ExecuteWrapper alloc] init];
executeWrapper.block = block;
[executeWrapperArray addObject:executeWrapper];
}
#end
In client code
[NSObject executeWhenDealloc:labelA block:^{
// clean up code
}];
Note: Keep in mind that don't hold a strong ref to label A in the label

IBOutlets properties between two classes doesn't retain. Deallocated?

I'm trying to figure out why my NSTextFields are only retained in the first method sendVarsToButton but not in the updateTotal method. I need to access the values from the TextField set in the first method but I can't because it seems like my IBOutlets deallocate themselves after the sendVarsToButton method. Can you help me please!?
Here's my .h
#import <Cocoa/Cocoa.h>
#import "TransactionViewController.h"
#class TransactionButtonModel;
#interface TransactionButtonController : TransactionViewController
{
NSMutableArray *buttonsArrays;
TransactionViewController *transactionViewController;
TransactionButtonModel *transactionButtonModel;
}
#property(nonatomic,retain) IBOutlet NSTextField *nom;
#property(nonatomic,retain) IBOutlet NSTextField *descriptionText;
#property(nonatomic,retain) IBOutlet NSTextField *prix;
#property(nonatomic,retain) IBOutlet NSTextField *CPUField;
#property(nonatomic,retain) IBOutlet NSTextField *quantite;
#property(nonatomic,retain) IBOutlet NSTextField *total;
-(void)sendVarsToButton:(NSString *)name:(NSString *)description:(double)price:(double)CPU:(long)tag;
-(void)updateTotal:(int)newQuantity;
-(void)addQuantiteToExistingProduct:(long)tag;
-(IBAction)removeProductFromView:(id)sender;
Here's my .m
#import "TransactionButtonController.h"
#import "TransactionViewController.h"
#import "TransactionButtonModel.h"
#implementation TransactionButtonController
#synthesize prix;
#synthesize nom;
#synthesize descriptionText;
#synthesize CPUField;
#synthesize total;
#synthesize quantite;
//In this method, everything works fine
-(void)sendVarsToButton:(NSString *)name :(NSString *)description :(double)price :(double)CPU:(long)tag
{
[nom setTag:tag];
[descriptionText setTag:tag];
[prix setTag:tag];
[CPUField setTag:tag];
[quantite setTag:tag];
[total setTag:tag];
nom.stringValue = name;
descriptionText.stringValue = description;
[prix setDoubleValue : price];
CPUField.doubleValue = CPU;
total.doubleValue = [TransactionButtonModel calculateButtonTotal:quantite.intValue :prix.doubleValue];
NSLog(#"retain! :%lu",[[prix viewWithTag:tag] retainCount]); // returns 2
[transactionButtonModel release];
}
-(void)updateTotal:(int)newQuantity
{
NSLog(#"retain! :%lu",[[prix viewWithTag:2] retainCount]); //returns 0
[total setDoubleValue:[TransactionButtonModel calculateButtonTotal:newQuantity :prix.doubleValue]]; // value of prix = 0 and prix = null
NSLog(#"Updated! :%i",newQuantity);
}
-(void)dealloc
{
[nom release];
[quantite release];
[prix release];
[total release];
[descriptionText release];
}
Thanks in advance.
It sounds like you have a few problems here. In no particular order:
You're releasing transactionButtonModel in sendVarsToButton::::: even though you didn't create it there and there's no particularly obvious reason you'd want to. This seems like probably a memory management error, but it's hard to say without context.
You're looking to the reference counting mechanisms to see why a variable is null. Overreleasing an object does not set variables referencing that object to null — it would just be a junk pointer and probably crash your program. The most likely reasons for a variable to be unexpectedly null are a) methods running in a different order than you expect, or b) having two different instances of your class that you're treating as though they're the same one. My money would be on B in this case. You're probably creating two instances of this class, one of which is actually showing the view and the other being essentially "blank." Try logging self in the two methods and see if it's the same object.
In one method you're logging viewWithTag:tag and in the other you're logging viewWithTag:2 — it's not necessarily a safe assumption that tag is 2.
prix is an NSTextField — why are you asking it for subviews? NSTextField isn't generally expected to have any useful subviews. There seems to be something odd in this design.
Your method names are nuts with all those bare colons. It's hard to read and often leads to mistakes down the line (due to the likelihood of misreading the code when you're not "in the moment" and the fact that it violates the language's idioms).
You're depending on retainCount to track memory management. The value returned by retainCount is questionable at best and often downright deceptive because things get retained and autoreleased all the time, and retainCount won't give you enough information to account for that. If you have a memory management problem (which doesn't appear to be this case with your variable becoming nil), a good approach would be to use Instruments and the debugger to track it down, not random retainCount logging.

How to retain my own objects and properties

I'm not sure I understood how alloc and retain work.
Recently I discovered that the NSString properties were not retained and I had to add [myString copy] when I set them. Which makes me wonder if I misunderstood the whole way of using retain/alloc
Please, may someone tell me if I'm doing it correctly? I read a lot and had a look on open source projects, this let me thing that I may have been wrong since the beginning.
Here is my way of doing it:
/**** VIEW.h *****/
#import "MyClass.h"
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
#property (nonatomic, retain) MyClass *myObject;
- (void)defineObject;
#end
.
/**** VIEW.m *****/
#import "VIEW.h"
#implementation MyViewController
#dynamic myObject;
- (void)viewDidLoad
{
[super viewDidLoad];
[self defineObject];
NSLog(#"My object's name is: %#", myObject.name);
}
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
.
/**** MyClass.h *****/
#interface MyClass : NSObject {
//not sure if this line is still necessary
NSString *name;
}
#property (nonatomic, retain) NSString *name;
- (id)initPersonalised;
- (void)setName:(NSString *)name;
- (NSString *)name;
#end
.
/**** MyClass.m *****/
#import "MyClass.h"
#implementation MyClass
#dynamic name;
(id)initPersonalised{
self = [super init];
name = #"Undefined";
}
- (void)setName:(NSString *)name{
self.name = [name copy];
}
- (NSString *)name{
return [self.name copy];
}
#end
I hope you can bring a bit of light, after months of programming this way, I'm less and less sure of doing it well.
This is indeed a topic that every Objective C programmer stumbles upon. There are a few things one needs to know:
Instance variable vs. property access
Within MyViewController,
myObject = xxx;
and
self.myObject = xxx;
are two different things. The first directly assigns to the instance variable and does neither release to old referenced insance nor retain the newly assigned instance. The latter one uses the property setter and thus releases the old and retains the new value.
Deallocation
Even when you have declared an implemented a property that takes care of retaining and releases the values, it won't take care of deallocation when your object (MyViewController in your case) is released. So you must explicitly release it in dealloc:
-(void) dealloc {
[myObject release];
[super dealloc];
}
Now to your code:
The snippet:
myObject = [[MyClass alloc] initPersonalised];
is perfectly okay. When you create an object, you use the pair of alloc and initXXX. The always create an instance with the reference count set to 1. So by directly assigning it to the instance variable, you create a clean constellation. I don't see no other way of creating the instance.
In MyClass you could use #synthesize name instead of #dynamic. Then the compiler would implement name and setName: automatically and you wouldn't need to do it yourself.
Finally, your missing dealloc.
Update:
If you use:
self.myObject = [[MyClass alloc] initPersonalised];
then you have a memory leak because initPesonalised sets the reference count to 1 and the setter of myObject increases it to two. If you want to use the setter, then I has to be:
MyClass* mo = [[MyClass alloc] initPersonalised];
self.myObject = [[MyClass alloc] initPersonalised];
[mo release];
It would be different if you weren't using initXXX to create a new instance. The class NSString for example has many methods called stringXXX, which create a new instance (or return a shared one) that has (conceptually) a reference count of 1 that will later automatically decreased by one. Then you better use the setter:
self.name = [NSString stringWithFormat: #"instance %d", cnt];
If you want to use copy instead of retain for your string property (which is good practice), then you can simply declare your property like this:
#property (nonatomic, copy) NSString *name;
When you then use #synthesize to implement the getter and setter, the compiler will generate them using copy instead of retain.
And NSString *name; is necessary even if you use #property and/or #synthesize to implement the property.
Alloc and init are methods that always go hand-in-hand. alloc allocates space for your object, and init initializes your object to some value. When you call alloc, you are responsible for freeing that object later. If you call copy, you are also responsible for releasing that object later. It's considered good practice to always initialize your objects right after you allocate them.
Now, to answer the questions I found in your code.
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
So is that line necessary? That depends. Does it make sense that your object has a MyClass as a property? This is a question only you can answer based on your design. I recommend you to study Object-Oriented Programming in more depth.
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
Not necessarily. You are just providing a pointer to an object of the specified kind. The moment you set your property, depending on the property modifiers, your class will know what to do with MyObject.
In that way, there's no need to call [yourObject copy]. In this way your properties will be copied instead of being retained. Just don't forget to release it later in your -dealloc method, like you would with retain properties.
All in all, this is what I recommend you to study a bit more:
Object-Oriented Programming (not related to your issue, but I can tell you are not comfortable using it. Objective-C is heavily object oriented, so you want to understand OOP).
iOS Memory Management.
You can have a look at the Memory Management Guide. It will help you to better understand the alloc & retain concepts; hope this helps you.

What UIView setter does #synthesize create

How to make custom UIView setter. For example:
1) We create property:
#property (retain) IBOutlet UILabel *myLabel
2) we make setter (the same as #synthesize would create):
- (void)setMyLabel:(UILabel *)anObject
{
[myLabel release];
myLabel = [anObject retain];
}
Is it correct, or should I check if the new view are not the same as the current with
- (void)setMyLabel:(UILabel *)anObject
{
if(anObject != myView){
[myLabel release];
myLabel = [anObject retain];
}
}
Just myView and anObject are object pointers. So should we check them with -isEqual method then? Or we don't need to check it at all? What code does #synthesize generates by defaults?
Thanks.
Only the second version (with the if statement) is correct. In your first version, imagine that anObject and myLabel actually point to the same object (i.e., the pointers are the same). In that case, you would release the object, which would cause it to be deallocated if no other object had retained it. The subsequent attempt to retain the deallocated object would cause a crash.

Automatic iVars with #synthesize

I understand that starting with iOS 4, there is now the ability to not declare iVars at all, and allow the compiler to automatically create them for you when you synthesize the property. However, I cannot find any documentation from Apple on this feature.
Also, is there any documentation on best practices or Apple recommended guidelines on using iVars and properties? I have always use properties like this:
.h file
#interface myClass {
NSIndexPath *_indexPath
}
#property(nonatomic, retain) NSIndexPath *indexPath
#end
.m file
#implementation myClass
#synthesize indexPath = _indexPath;
- (void)dealloc {
[_indexPath release];
}
#end
I use the _indexPath instead of indexPath as my iVar name to make sure that I don't ever use indexPath when I need to use self.indexPath. But now that iOS supports automatic properties, I don't need to worry about that. However, if I leave out the iVar declaration, how should I handle releasing it in my dealloc? I was taught to use iVars directly when releasing in dealloc, rather than using the property methods. If I don't have an iVar at design-time, can I just call the property method instead?
I've went through many different ways of dealing with this. My current method is to use the property access in dealloc. The reasons not to are too contrived (in my mind) to not do it, except in cases where I know the property has odd behavior.
#interface Class
#property (nonatomic, retain) id prop;
#end
#implementation Class
#synthesize prop;
- (void)dealloc;
{
self.prop = nil;
//[prop release], prop=nil; works as well, even without doing an explicit iVar
[super dealloc];
}
#end
In constrast, I do the following:
#interface SomeViewController : UIViewController
#property (nonatomic, copy) NSString *someString;
#end
and then
#implementation SomeViewController
#synthesize someString;
- (void)dealloc
{
[someString release], someString = nil;
self.someString = nil; // Needed?
[super dealloc];
}
#end
Note: At some point Apple will enable synthesize-by-default which will no longer require the #synthesize directive.
You can directly access instance variables using -> symbol instead of dot . (which will invoke ivar's corresponding accessor method):
.h
#interface myClass {
}
#property(nonatomic, retain) NSIndexPath *indexPath
#end
.m
#implementation myClass
- (void)dealloc {
[self->indexPath release];
self->indexPath = nil; // optional, if you need it
[super dealloc];
}
#end
Thus you will directly access iVar and not it's corresponding accessor method, obtaining additional benefit - performance.