Why the setter is called for one property but not the other? - objective-c

I took this code from the Big Nerd Ranch iOS Programming book. In the code, they are assigning two instance variables, coordinate and title. Why is coordinate assigned directly, and title is set by calling a setter?
Header File
#interface BNRMapPoint : NSObject<MKAnnotation>
-(id)initWithCoordinate:(CLLocationCoordinate2D )c title:(NSString *)t;
#property(nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString *title;
#end
Implementation File
-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];
if(self){
coordinate = c;
[self setTitle:t];
}
return self;
}

Two reasons, the most important of which is that there is no setter for the coordinate property. It's declared read-only, so there is only a getter method generated.
The second is that CLLocationCoordinate2D is a struct, not an object. There are memory management actions (copying, in this case) that have to be taken for the title object; the simplest way to make that happen is to use the already-existent setter method. The compiler takes care of moving the data for a POD type like CLLocationCoordinate2D.
If the second were the only reason, however, this would be a poor decision -- that's bad style to use the setter for one property and not for the other.

There is a school of thought that says you should copy NSStrings. They're invoking the setter on the string to get that copy. However, there's no need to copy (or even retain) the coordinate, as Josh points out.

Related

Accessing Objects from Class or Method

I have 2 classes in my Cocoa project (Xcode). First is AppDelegate class and the second is a Book class.
In my Book class, I set an integer property in the #interface which is the book's chapters. In its #implementation, I have created objects (ex. Book *firstBook = [[Book alloc]init]) and set their properties (In the Book.m file). Those are my data and will not change.
In my app delegate, I have a method that will fetch what the user selected from an interface item, get the title of the selected item, who's name will be identical to one of those in Book.m. Then a for loop will run to create menu items for a popUpButton so the user can select which chapter to jump to.
The problem I am seeing now is that when I try running the for loop to create menu items, I need the limit amount of the loops. That limit amount is based on the selectedObjectByUser's chapter property (listed in Book.m). How do I access that.
I am sure it will work if I can connect these two together because it works when create the object inside this method(under AppDelegate.h) but the problem is that it is too space consuming and it changes often.
I'm not entirely sure what the situation is here, but let's take a look at some sample code first.
//// Book.h
#interface Book : NSObject
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *author;
#property (nonatomic, assign) NSInteger numberOfPages;
- (id)initWithTitle:(NSString *)aTitle andAuthor:(NSString *)anAuthor;
#end
//// Book.m
#implementation Book
- (id)initWithTitle:(NSString *)aTitle andAuthor:(NSString *)anAuthor {
if ( self = [super init] ) {
self.title = aTitle;
self.author = anAuthor;
}
return self;
}
- (void)dealloc {
self.title = nil;
self.author = nil;
[super dealloc];
}
#end
So in this we establish a class and provide it with 3 properties, title and author (which are both NSString's) and numberOfPages (which is an integer). Within the class we can manipulate those values by calling things such as self.propertyName = value.
This is all well and good, but what actually is happening? Well let's update the header a little more:
//// Book.h
#interface Book : NSObject {
#private
NSString *_title;
NSString *_author;
NSInteger _numberOfPages;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *author;
#property (nonatomic, assign) NSInteger numberOfPages;
- (id)initWithTitle:(NSString *)aTitle andAuthor:(NSString *)anAuthor;
#end
In this, we have just explicitly defined something that the compiler will normally infer through the #property construct. These new additions are what we call instance variables, or ivars, and are where the values you assign to your properties are actually stored.
However, manipulating the ivars can be dangerous if you are not yet 100% comfortable with memory management. Even if you are using ARC, you should still understand how that management works.
So we've now exposed where these properties actually store there data, but what about that #private job? What's it all about? #private is part of a family of keywords that help to denote the "Accessibility Scope" of something. The other two keywords in this family are #protected and #public, however the use of the second two is infrequent, if not unusual. These keywords are responsible for saying where you are allowed to access things. Here's a quick definition of them.
#public Freely accessible from anywhere, even outside of the object itself. However accessing an instance variable directly from outside of its own class is generally considered to be extremely bad practice in the Cocoa development world, hence why you'll find very little on how to do it.
#protected Freely accessible within the class and its own subclasses. Can not be accessed outside of the class/object.
#private Freely accessible within the class, but not anywhere else. Can not be accessed outside of the class/object or even in its subclasses.
So now that we've covered what is actually driving the storage behind a property, let's take a look at using our Book object in another part of the app, such as AppDelegate.
//// AppDelegate.m
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
Book *myBook = [[Book alloc] initWithTitle:#"pending title" andAuthor:#"Foo Bar"];
myBook.title = #"My Cool Book";
NSLog(#"%# by %#", myBook.title, myBook.author);
[myBook release];
}
#end
In this we create a new Book object, to put it in more technical terms, we define a variable called myBook with a type of Book and instantiate it. In this we're using the -initWithTitle:andAuthor: method that we created earlier in order to tell the Book object that it should have an initial title and author.
Following this line we then arrive something a little more curious. myBook.title = #"My Cool Book"; You might recall that we had something similar back in Book.m, self.title = aTitle. So what is going on here? Why are we using myBook now rather than self, like we did previously? The reason is because of what self actually is.
self is a keyword provided by the Objective-C runtime, and refers to the current object that you are within. So if we write code inside Book.m, self will refer to the current Book object. If we use self within AppDelegate.m, it will refer to the AppDelegate. So in our earlier code, self was referring to the current Book object much like our myBook object is now referring to a specific Book object. They essentially are equal to each other (not exactly, but thats another area of discussion).
This means any of the properties within Book or methods can be accessed through the myBook variable, much like you would using self inside of Book.m. So we could also do
myBook.title = #"My Book";
myBook.author = #"Baz Quux";
myBook.numberOfPages = 100;
Hope this helps (and answered your question, if not then may it serve as a reference to people wishing to know more about properties and instance variables)

Objective-C : Need advice on setting instance variables in init method

I am using ARC.
This is my .h file
...
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
...
This is my .m file
....
#synthesize coordinate, title;
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];
if (self) {
coordinate = c;
[self setTitle:t];
}
return self;
}
....
Is setting coordinate this way, the right way to do it? Given that I declare it as readonly, it seems like it is the only way to do it. What if I just use the default (i.e. readwrite), in this case, should I use the setter method [self setCoordinate] instead?
I could set the title by doing title = t as well. Compare to using the setter method, the result is the same, but what is the difference ?
Thanks! Wish I could accept all of your answers.
You're actually supposed to set ivars directly in an initializer method all the time. This is true whether or not you have a readonly or readwrite property. The documentation here even says so.
The reasoning behind this has to do with inheritance. If someone were to subclass your class and overwrite the setters for your properties such that they bypass the ivars you created (or do some other wacky thing), then suddenly your original implementation of your initializer method now no longer does what it is written to do. In particular, your initializer could end up creating an object with a weird state due to the subclass overriding your accessors. In the pre-ARC days, you could also end up with tricky (or just straight-up broken) memory situations when this sort of thing happens. The take-away message is: you should write initializers so that they will always create an object with a known valid state.
So (assuming you're using ARC) your initializer should actually be:
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];
if (self) {
coordinate = c;
title = [t copy];
}
return self;
}
Personally, I prefer to synthesize ivars with a starting underscore to clarify when I'm using the property and when I'm accessing the ivar directly (LLVM 4.0 now does this to automatically synthesized properties as well).
#synthesize coordinate = _coordinate;
#synthesize title = _title;
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];
if (self) {
_coordinate = c;
_title = [t copy];
}
return self;
}
1: As your code is now, yes, that is the right way to do it. If you weren't using ARC (assuming you are currently), you'd also want to retain the value to assert ownership. This will be done automatically under ARC. Keep in mind that that is not the only way of doing it; you could redeclare the property as readwrite in the class extension in the implementation file. This is a common practice which allows you to have the benefits of a readwrite property while having the property still be readonly to users of the class. Ex.
//MyClass.h
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSNumber* number;
- (void) initWithNumber:(NSNumber*)number;
#end
//MyClass.m
#interface MyClass ()
#property (nonatomic, strong, readwrite) NSNumber* number;
#end
#implementation MyClass
//this changes the instance variable backing the property to _number.
#synthesize number = _number;
- (void) initWithNumber:(NSNumber*)number{
self = [super init];
if (self) {
self.number = number;
}
return self;
}
#end
At the end of the day, I'd say it's a good habit to use setters whenever you can to keep things KVO compliant and so that you always know when values change. For instance, if you have a custom UIView with a property that is reflected in its appearance, chances are you'd want to redisplay yourself when it changes. The easiest way to do this is to implement the setter yourself and call setNeedsDisplay after setting the value. You couldn't do that if you set the instance value backing the property directly; the user of the class would have to remember to call setneedsDisplay every time they set it, manually.
2: One goes through the setter method, giving you a way to know when a value is going to be set, while one sets a value to the instance variable backing the property. The setter method will always handle memory management in the way it was told to, while it's up to you to do things such as copying values for a copy setter if you assign directly to an instance variable, so that you maintain some consistent scheme. Going through setters sometimes, and not others can lead to some nasty bugs if you don't be careful. Never going through setters makes it hard to know when values change, making it near impossible to weed out invalid values. For instance, if you had an int property you wanted to limit to values in some range and someone passed in a value under the minimum limit, you'd probably want to set the property to the lowest possible value in the range. You can't do that without the value going through the setter first.
Yes, it is fine to set it like that. If you prefer to use a property all the time you can override the property to be read/write rather than read-only in a class extension. In Foo.m:
#interface Foo ()
#property (nonatomic) CLLocationCoordinate2D coordinate;
#end
#implementation Foo {
// ...
self.coordinate = c;
}
Setting the coordinate that way is correct, and is the only way to do it if you have declared the property readonly.
Setting the title using title = t is different than setting the title using [self setTitle:t]. If you directly assign to the instance variable, you will just retain the NSString instance that was passed as argument t. But if you using the accessor method, the accessor will ask the string to copy itself (because you declared the property copy). If the string you were given as argument t is actually an NSMutableString, then you will get an immutable copy of it. If the string you were given as argument t is already an immutable string, it will just return itself when asked for a copy.
self.coordinate = c;
is essentially compiled to be the same as calling
[self setCoordinate:c];
The difference between coordinate = c and [self setCoordinate:c]; is that the first is just setting a variable directly where as the second is calling a method.
The reason to be wary is that methods could potentially have side effects depending on how the implementation is written e.g. (stupid example)
- (void)setCoordinate:(CLLocationCoordinate2D)coordinate;
{
_coordinate = coordinate;
[self doSomethingCrazy];
}

Features of use #property and #synthesize (cocos2d)

I saw in the libraries for use cocos2d strange #property and #synthesize
Standard in the examples is written as follows:
in .h
CGFloat minimumTouchLengthToSlide;
}
#property(readwrite, assign) CGFloat minimumTouchLengthToSlide;
in .m
#synthesize minimumTouchLengthToSlide
But in lib https://github.com/cocos2d/cocos2d-iphone-extensions/tree/master/Extensions/CCScrollLayer and another libs\extensions
in .h
CGFloat minimumTouchLengthToSlide_;
}
#property(readwrite, assign) CGFloat minimumTouchLengthToSlide;
in .m
#synthesize minimumTouchLengthToSlide = minimumTouchLengthToSlide_;
What is the meaning of this code?
Why they changed minimumTouchLengthToSlide to minimumTouchLengthToSlide_ and added minimumTouchLengthToSlide = minimumTouchLengthToSlide_;
Its often considered good practice to name the instance variable different from the property. The resoning behind this is that in that case you cannot accidently use the instance variable instead of the property. This is not that important when using value types such as integers and floats but more important when using reference types on retain properties. Consider a property
#property (nonatomic, retain) NSString *myString;
...
#synthesize myString;
The compiler takes care of retaining the string when you do self.myString = someString. But when you write myString = someString you do not actually use the property but rather the variable directly and no retaining will take place. This can lead to zombies, leaks etc. By giving the instance variable a different name like this:
#property (nonatomic, retain) NSString *myString;
...
#synthesize myString = myString_;
you can no longer write myString = someString because this would issue a compiler error. If you needed to use the instance variable directly you could always write _myString = someString but in practice this is rarely needed.
There are other cases when you write explicit property methods but the issue is basically the same, you cannot accidently bypass the property methods when using the second variant.
So basically this is a method to avoid unnecessary errors when handling properties (mostly retain-properties).
#property and #synthesize are a really cool feature of Objective-C to allow the automatic creation of getter and setter methods. In your examples they would create:
- (CGFloat)minimumTouchLengthToSlide and
- (void)setMinimumTouchLengthToSlide:(CGFloat)newLength; for free.
#synthesize minimumTouchLengthToSlide = minimumTouchLengthToSlide_ means they are telling Objective-C that when someone tries to access that property, then it should point at the instance variable minimumTouchLengthToSlide_
readwrite,assign describe what happens when someone sets the property. Assign means that the value is not retained, the variable is just pointed. An example of what that method might look like could be this:
- (void)setMinimumLengthToSlide:(CGFloat)newLength {
[self willChangeValueForKey:#"minimumLengthToSlide"]; // let observers know this property is changing
minimumLengthToSlide_ = newLength;
[self didChangeValueForKey:#"minimumLenghtToSlide"];
}
You can read more about them here.

property without retain with example

if we use a property without the "retain", what does it change? i have this example :
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithCoordinate:(CLLocationCoordinate2D) coordinate;
in the .m :
-(id)initWithCoordinate:(CLLocationCoordinate2D)coord{
coordinate = coord;
return self;
}
the "retain" is normally used for the setter, isn't it? so here, we use the setter, in initWith..., but we don't use "retain"... any idea?
Thanks
CLLocationCoordinate2D is not an Objective C object, so attempting to send retain and release to it doesn't make sense.
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
This declares an assign read only property, which is the only correct thing to do for a property with a plain C type. Additionally you have said it is nonatomic which means that there is no code to synchronize the property. Since the property is a struct consisting of two 64 bit values, that probably means that you can get an inconsistent result back if you read the property at the same time as some other thread is changing it.
the "retain" is normally used for the setter, isn't it? so here, we use the setter, in initWith...
No, you don't actually. The line
coordinate = coord;
actually assigns the instance variable directly. However, this is what you want in this case. If the property was not read/write and was an Objective-C object type, it would still be assigning the instance variable directly. In that case, you'd need one of the following.
[self setCoordinate: coord];
or
self.coordinate = coord;
or
coordinate = [coord retain]; // in init only
By the way, your init is wrong. It should follow the pattern:
-(id)initWithCoordinate:(CLLocationCoordinate2D)coord{
self = [super init]; // assuming init is the designated initialiser of the super class
if (self != nil)
{
coordinate = coord;
}
return self;
}
In your specific case, readonly means that you class allocates the object and then gives access to it through a property that does not allow this property to be changed from outside.
In general, the object will be nevertheless retained by the class when it allocates it, and released when dealloc is executed.
In your case, the object of type CLLocationCoordinate2D is copied when assigning to the ivar:
coordinate = coord;
because it is not a pointer object, rather a simple struct made out of 2 doubles:
typedef double CLLocationDegrees;
typedef struct {
CLLocationDegrees latitude;
CLLocationDegrees longitude;
} CLLocationCoordinate2D;
So, that is the reason why you don't see any retain in the code. If instead of being a CLLocationCoordinate2D object, it had been an NSString you would have probably needed a retain when doing the assignment to the ivar. (I say "probably" because it all depends on the ownership of the assigned object).
The retain property is used for objects that need to be (surprise) retained. That is: your objects needs the object assigned to the property to stay around, it may not be deallocated.
In your case, it wouldn't change anything as a struct, not an object.
But what you're using in the cited code is readonly, and that means there is no setter, only a getter.

Objective-C setter/getter naming conventions drive me mad?

I have been trying to understand something for several hours and I would like to get your point of view.
I have setter/getter on one of my class properties (I noticed that I MUST add "set" in front of the setter name else the compiler says that there is no setter):
#property (nonatomic, retain, readwrite, setter=setTopString:, getter=TopString) NSString* m_topString;
When I call the setter like this, the compiler is happy:
[secureKeyboardController setTopString:#"This action requires that your enter your authentication code."];
But when I try to use the "dot" convention, then I am rejected by the compiler:
secureKeyboardController.topString = #"This action requires that your enter your authentication code.";
What is really weird is that the dot naming convention works fine with this property:
#property (nonatomic, readwrite, getter=PINMaxLength, setter=setPINMaxLength:) NSInteger m_PINMaxLength;
In this case i can do:
[secureKeyboardController setPINMaxLength:10];enter code here
or
secureKeyboardController.PINMaxLength = 10;
In both cases, the compiler is happy.
I really would like to fall asleep tonigh less stupid than I currently feel now. Thus any explanation would be greatly appreciated.
Regards,
Apple92
What you're doing is declaring properties as if you were declaring instance variables. You should not be using the names in the getter and setter attributes on the #property declaration with dot syntax; that it happens to be working now is not - so far as I know - by design.
The property should be what you use with dot syntax. For some reason - unfamiliarity with Cocoa coding conventions, I expect - you named your properties m_topString and m_PINMaxLength. That means you should use them as someObject.m_topString and someObject.m_PINMaxLength.
If you want to use those names for the instance variables that you've decided to use for the properties' backing storage, you should declare that in the #synthesize directive instead.
This is how your class should look, to be more in line with regular Cocoa and Objective-C coding conventions:
#interface SomeClass : NSObject {
#private
NSString *m_topString;
}
#property (nonatomic, readwrite, copy) NSString *topString;
- (id)initWithTopString:(NSString *)initialTopString;
#end
#implementation SomeClass
#synthesize topString = m_topString;
// this says to use the instance variable m_topString
// for the property topString's storage
- (id)initWithTopString:(NSString *)initialTopString {
if ((self = [super init])) {
m_topString = [initialTopString copy];
// use the ivar directly in -init, not the property
}
return self;
}
- (void)dealloc {
[m_topString release];
// use the ivar directly in -dealloc, not the property
[super dealloc];
}
- (NSString *)description {
return [NSString stringWithFormat:#"SomeClass (%#)", self.topString];
// elsewhere in your class, use the property
// this will call through its getter and setter methods
}
#end
You are trying to fight the compiler, and the compiler fights back.
You are trying to declare a property named m_topString with setter setTopString and getter TopString, and that is plainly stupid. You are writing Objective-C code, not C++. Your code will be a maintenance nightmare (unless the next maintainer is just sensible and changes your code to Objective-C conventions).
Do yourself a favour, start writing Objective-C code. Just call the property topString, don't pick your own names for the setter and getter, don't pick your own names for the instance variable, and everything works just fine.
Capitalize the T in TopString, i.e. secureKeyboardController.TopString
I'm 90% sure that will fix your problem.