Proper way to use instance variables/property/synthetize with ARC [duplicate] - ios

This question already has answers here:
Declaration/definition of variables locations in ObjectiveC?
(4 answers)
Closed 9 years ago.
What is the proper way to work with instance variables (declared on interface), their #property and #synthesize, when working in ARC project? What I now do is following:
SomeClass.h:
#interface SomeClass : NSObject {
NSString *someString;
}
#property(nonatomic, copy) NSString* someString;
and SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
self.someString = #"Foobar";
}
The thing is that there are other approaches that works, like using just the #property:
SomeClass.h:
#interface SomeClass : NSObject
#property(nonatomic, copy) NSString* someString;
Accessing the someString without self:
SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
someString = #"Foobar";
}
etc. I'm new to Objective-c, I'm used to Java. What is the proper way to work with attributes then? I understand that special cases will have special behavior, but what is the best approach in general? (by general I mean I want to access the variable from the class itself and from "outside" and I want ARC to still work correctly, eg. I don't have to worry about memory leaks)

For simple properties, you don't need the instance variable declaration or the #synthesize. The clang compiler will generate both for you by default. So you could write this in the header:
#interface SomeClass : NSObject
#property (nonatomic, copy) NSString *someString;
#end
And the implementation:
#implementation SomeClass
- (void)someMethod {
self.someString = #"Foobar";
}
#end
Avoid direct instance variable access unless you are in the -init method or overriding the setter. Everywhere else you should use the dot syntax (self.someString). If you do need access to the instance variable, the default synthesize will create an underscore-prefixed ivar, e.g. _someString.
Note that for classes with mutable versions like NSString/NSMutableString and NSArray/NSMutableArray the standard practice is to use a copy property. If you use strong on a string or array, the caller might pass in a mutable version and then mutate it from under you, causing hard-to-find bugs.

Check out this SO post for information about ARC.
(Edited) The "strong" attribute tells ARC to keep an object around until the object with the property is deallocated. You do need the "copy" attribute because an NSString property could have been passed in as an NSMutableString. The "copy" guarantees that the original object will be kept around. Again, I apologize for the incorrect/misleading information I originally had here.
The reason you can access the instance variable someString as well as the property self.someString is that the #synthesize someString line creates an instance variable for the property and creates methods for getting and setting the value of it. However, it is recommended that you use the property instead of directly using the instance variable because by using the instance variable, you cannot let the parent object know that you've changed one of its properties.

Related

Is it recommended to define ivars for readonly synthesized properties?

I have come to find that many of the times in which I want to have a synthesized readonly property, I merely implement the getter method of that property in terms of other variables with no need for an ivar, for example (Note: I am defining ivars in the interface because I am using OmniGraffle UML software and it does not recognize ivars auto-generated by synthesized properties):
#interface Editor : UIView {
BOOL _wordWrap;
BOOL _showLineNumbers;
NSDictionary *_options;
}
#property (nonatomic) BOOL wordWrap;
#property (nonatomic) BOOL showLineNumbers;
#property (nonatomic, copy, readonly) NSDictionary *options;
#end
#implementation Editor
#synthesize wordWrap = _wordWrap;
#synthesize showLineNumbers = _showLineNumbers;
#synthesize options = _options;
- (NSDictionary *)options {
return #{
#"WordWrap" : [NSNumber numberWithBool:self.wordWrap],
#"ShowLineNumbers" : [NSNumber numberWithBool:self.showLineNumbers],
};
}
#end
In the above Editor class, is it necessary for me to define the _options ivar in the header definition and more importantly does the auto-generated ivar take up memory or space in the symbol table? Also, would it be more efficient to use copy, retain, or no value in this case? Just curious.
First: stop putting your ivar declarations in your #interface. They belong in your #implementation. See this answer for a detailed explanation.
Anyway, given what you've written, your #synthesize options = _options has no effect.
That #synthesize has two possible effects:
It adds an instance variable named _options, if your class doesn't have one.
It generates a getter method, options, that returns the value of _options, if your class doesn't have a method named options.
Since you manually defined the instance variable and the getter, the #synthesize does nothing. You can remove it entirely without changing the meaning of your program.
Specifying copy on a readonly property has no effect. The copy and retain (or, more properly under ARC, strong) attributes only affect the generated setter method, and the compiler doesn't generate a setter for a readonly property. (If you change the property to readwrite in a class extension, then copy matters.)
Yes, the _options ivar takes up both memory (for each instance of Editor) and space in the symbol table.
Since you're not using the _options ivar, you should delete it entirely. You should also delete the #synthesize entirely, so the compiler doesn't generate the _options ivar for you.

iOS programming with xCode 5.1.1 [duplicate]

This question already has answers here:
property not working with getter AND setter
(4 answers)
Closed 8 years ago.
So I wanted to write a simple class.
header file
#import <Foundation/Foundation.h>
#interface Player : NSObject
#property (readonly, copy) NSString *PlayerName;
#end
class file
#import "Player.h"
#implementation Player
- (NSString*) PlayerName
{
return _PlayerName;
}
- (void)setPlayerName:(NSString *)PlayerName
{
_PlayerName = PlayerName;
}
#end
But now Xcode gives me an error, saying that the _PlayerName variable does not exist. But I thought that's the way you need to write properties access functions.
As soon as you override both the setter and getter the compiler no longer creates them for you. Since the compiler is not doing anything with the property it will no longer create the the private ivar.
So unless you need to override the setter or/and getter you better of letting the compiler create them for you.
But if you need just add #synthesize PlayerName = _PlayerName; just after the #implementation.
If you declare the setter AND the getter, you NEED to synthesize the variable, since compiler won't do it automatically for you in this case.
#synthesize PlayerName = _PlayerName;
Also you specified your property as a readonly which is not the best practice to use when you want to save something to that variable, because with dot notation you will get complier error.

How to mark an Object as both Retain and Readonly in iOS using ARC?

I use the following to expose languages array.
#property(nonatomic,readonly)NSArray *languages;
Assigning languages before ARC was like this:
languages=[[NSArray arrayWithObjects:
[[Language alloc]initWithCode:#"es"],
[[Language alloc]initWithCode:#"en"],
nil] retain];
So, I was both able to retain the object and also mark it as readonly to outside.
With ARC,
As I cannot type "retain" manually. How can I do this without overriding setters and getters? Is there a way to mark a property both readonly (to outside) and retain (to inside) for ARC?
retain* and readonly really have nothing to do with each other. readonly indicates that the property only has a getter, not a setter. retain means that the class maintains a retaining (strong) reference to the object referenced by the property, and under ARC, assuming the property is backed by an underlying, synthesized instance variable, means that the instance variable is a __strong variable.
If you want a property that is read-only to the outside world, but can be read and written inside the class, you can redeclare the property as readwrite in a class extension in your implementation (.m) file. In the header:
#property (nonatomic, strong, readonly) NSArray *languages;
Then, at the top of your .m:
#interface YourClass ()
#property (nonatomic, strong, readwrite) NSArray *languages;
#end
Inside the class's implementation, you can now use self.languages = ...;, (or _languages = ...; in the initializer) to set the languages property. This is not actually different under ARC vs. non-ARC...
*Under ARC, it's more customary to use strong instead of retain, but the compiler treats them the same way.

iOS "copy" keyword on Mutable objects

I've a theoretical doubt about two type of declaration of a mutable object in iOS (and MacOSX I think) with ARC.
What's the difference between a declaration of an NSMutableArray in the Class Extension, like the code below:
#interface MyViewController ()
#property (copy) NSMutableArray* myMutableArray;
#end
//Class implementation
#implementation MyViewController
...
- (void)viewDidLoad
{
_myMutableArray = [#[] mutableCopy];
}
and a declaration of the same array in this way
#interface MyViewController ()
#property (nonatomic, strong) NSMutableArray* myMutableArray;
#end
//Class implementation
#implementation MyViewController
...
- (void)viewDidLoad
{
_myMutableArray = [#[] mutableCopy];
}
Which one is better? I've seen both versions around and apparently both work fine. However I'd like to know which one is the best option.
I know that the "copy" keyword is to use copy for classes that are part of a class cluster that have mutable/immutable pairs. So in this case, it appear to be the right choice. But the use of the "copy" keyword and the "mutableCopy" property (like the first example) seems a duplicate to me. Am I wrong?
Thanks!
The strong property is the one to use. Since its a mutable object (and is declared as such) then you wouldn't want a copy making, since then things like [self.myArray addObject:object] wouldn't work. You'd use copy properties for immutable objects that may have mutable versions passed in (so an NSString would often be a copy property).
The way the arrays are assigned (making a mutable copy of an empty array made using objective-c literals) is pretty clumsy and would be better written as self.myMutableArray = [NSMutableArray array];
Also, don't access the instance variable directly, use the property accessor.

iOS : Other alternative to instance variable?

I have a project which others have written and I have taken over it, hoping to make the app better.
I encountered one problem:
From one class:
I write _customclass.variable. CustomClass is another class and variable is a property and is of int type. And I get value of the variable in this class, but when I change it to self.customclass.variable, I always get 0. Is there other alternative ways to get value from other class?
(a)
#property (readwrite)int boxSpacing;
(b)
#synthesize boxSpacing;
(c)
- (id)initWithCoder:(NSCoder *)aDecoder {
self.boxSpacing = 10;
}
You asked:
Is there other alternative ways to get value from other class?
The short answer is that using the "getter" is the customary way to get a value from another class. But looking at your problem (admitted, not having enough source code to properly diagnose your issue), I'm guessing that the issue rests in the use of instance variables. But more on that later.
First, let's look at the proper use of declared properties and their instance variables and their accessor methods (the getters and setters). Generally you should set properties using these accessor methods. You can, though, use access a variable from within a class using either the instance variable (and you should not use accessor methods in initializer and dealloc methods). And when using the getter and setter, you can choose whether to use the method invocation (e.g. "[self customObject]") or the dot notation (e.g. "self.customObject").
Let's look at an example. Let's assume you have some simple CustomClass:
#interface CustomClass : NSObject
{
// you don't need to declare the instance variable
//int _boxSpacing;
}
#property (nonatomic) int boxSpacing;
#end
#implementation CustomClass
// In Xcode 4.4 and later, the synthesize statement is optional, and if you
// omit it, it will synthesize the instance variable like this, with the
// leading underscore. While you don't need to use an underscore in your
// instance variable, it has become convention in iOS development and it's
// a good technique to minimize chances that you accidentally use the instance
// variable when you actual intended to use the property's accessor methods
// (the getter and setter).
#synthesize boxSpacing = _boxSpacing;
#end
Now, let's assume that you're going to use this CustomClass from within, for example, your view controller. So, first you declare and instance of this CustomClass:
#interface MyViewController : UIViewController
{
// you do not need this instance variable declaration
// the #synthesize statement will take care of this for you
// CustomClass *_customObject;
}
#property (nonatomic, strong) CustomClass *customObject;
#end
And then let's demonstrate how to use the value property of the CustomClass object customObject from within your view controller:
#implementation MyViewController
// Again, in Xcode 4.4 and later, the synthesize statement is optional, and if you
// omit it, it will synthesize the instance variable like this, with the
// leading underscore
#synthesize customObject = _customObject;
- (void)customClassTest
{
// initialize the object
self.customObject = [[CustomClass alloc] init];
// set the property
self.customObject.boxSpacing = 1;
// finally, let's demonstrate three ways to retrieve the value
NSLog(#"%d", self.customObject.boxSpacing);
NSLog(#"%d", [[self customObject] boxSpacing]);
NSLog(#"%d", _customObject.boxSpacing);
// while we're at it, let's demonstrate other ways to set the property
_customObject.boxSpacing = 2;
// or
[[self customObject] setBoxSpacing:3];
}
Ok, so let's get back to your problem. You say:
I write _customclass.variable. CustomClass is another class and variable is a property and is of int type. And I get value of the variable in this class, but when I change it to self.customclass.variable, I always get 0.
Ok, this can be caused by a couple of different problems, but the most common problem I see is confusion between explicitly declared instance variables and the instance variables created behind the scenes by the #synthesize statement. This is why I always advise that people not explicitly define the instance variables for their declared properties, but rather let the #synthesize statement do that automatically. That way I can't have the sort of problem I'm about to demonstrate.
Consider this innocuous (though incorrect) example:
#interface MyViewController : UIViewController
{
CustomClass *_customObject;
}
#property (nonatomic, strong) CustomClass *customObject;
#end
#implementation MyViewController
#synthesize customObject;
- (void)customClassTestError
{
// initialize the object
self.customObject = [[CustomClass alloc] init];
// this works
self.customObject.boxSpacing = 1;
// this doesn't!
_customObject.boxSpacing = 2;
// when it hits this statement, the value will still be 1!!!
NSLog(#"%d", self.customObject.boxSpacing);
}
Do you see the problem? While I declared an instance variable with the underscore, _customObject, when the compiler hit the #synthesize statement, it created another instance variable, this time without the leading underscore, customObject. Thus, my explicitly declared instance variable never received the init/alloc and therefore is nil and thus any attempts to use it won't work!
Typically we see the converse problem (an explicitly declared instance variable without the underscore and a #synthesize statement of the form #synthesize customObject = _customObject), but hopefully you'll get the idea.
Anyway, this is the most common example of what would cause the behavior you describe. If this isn't what's going on, please provide us a more extensive code sample.
But if you're having problems, I'd always suggest that you check the value of your CustomClass object, itself, before you try to access its properties. Make sure the class object itself has been properly initialized (whether for the reasons I list above, or some other initialization problem) before you try to use its properties. You can do something like NSLog(#"CustomClass object = %#", customObject); or NSAssert(customObject, #"Object not properly initialized");.
Have you defined a #property for the CustomClass? If so, have you assigned a value?
If not, then you're sending the message variable to nil. And in your case that will result to 0.

Resources