Why does this property need the 'retain'? - objective-c

Given the following definition of a class with retain properties:
#interface FeedEntry : NSObject<NSCoding>
{
NSURL* url;
NSData* source;
}
#property (retain) NSURL* url;
#property (retain) NSData* source;
#end
#implementation FeedEntry
#synthesize url;
#synthesize source;
-(void)encodeWithCoder:(NSCoder*)coder
{
[coder encodeObject:url forKey:#"url"];
[coder encodeObject:source forKey:#"source"];
}
Why does the url property in initWithCoder method need the "retain":
-(id)initWithCoder:(NSCoder*)coder
{
url = [[coder decodeObjectForKey:#"url"] retain];
source = [coder decodeObjectForKey:#"source"];
NSLog(#"got url=%#\n", url);
return self;
}
Specifically, why doesn't the synthesized "get url" method retain the object? (I'm guessing the source property will need a retain as well).

Quick answer:
When you set:
url = [[coder decodeObjectForKey:#"url"] retain];
you are not using the #property. You are manually setting the value of the instance variable url. You must, therefore, also manually retain the value.
To set the variable using the synthesized properties, you would instead call:
[self setUrl:[coder decodeObjectForKey:#"url"]];
or
self.url = [coder decodeObjectForKey:#"url"];
Either of these forms would make use of the synthesized methods, and handle the retain automatically.
Details:
In Objective-C, the #property and #synthesize keywords automatically create the getter and setter methods for you:
#interface MyClass
{
id someValue;
}
#property (retain) id someValue;
#end
#implementation MyClass
#synthesize someValue;
#end
Is equivalent to:
#interface MyClass
{
id someValue;
}
- (id)someValue;
- (void)setSomeValue:(id)newValue;
#end
#implementation MyClass
- (id)someValue { return someValue; }
- (void)setSomeValue:(id)newValue
{
[newValue retain];
[someValue release];
someValue = newValue;
}
#end
This creates an important distinction between the "internal" member variable and the property having the same name. If you reference the member variable by name, you are bypassing the synthesized property methods.

Related

Objective-c readonly copy properties and ivars

I'm try to grok properties declared as both copy and readonly in objective-c, and specifically, whether I have to do the copy myself. In my init methods. Evidence suggests I do:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData *test;
- (instancetype)initWithData:(NSData *)data;
#end
#implementation A
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
_test = data;
}
return self;
}
#end
int main (void) {
NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
A *a = [[A alloc] initWithData:d1];
NSLog(#"%lx", (unsigned long)d1);
NSLog(#"%lx", (unsigned long)a.test);
return 0;
}
I had thought I could do self.test = data in my init method, but that is not permitted because it's readonly (not unexpectedly). Of course, self.test = [data copy] ensures two different objects.
So: Is there a way to create a readonly property in objective-c that copies the incoming value, or is it sufficiently an edge case that the combination is pointless and I have to do any copying myself manually anyway?
A #property declaration is merely shorthand for some accessor/mutator method declarations, and (in some cases) synthesized implementations for said accessor/mutator methods.
In your case, the #property(nonatomic, copy, readonly) NSData *test declaration expands to this equivalent code:
#interface A : NSObject
{
NSData* _test;
}
- (NSData*)test;
#end
#implementation A
- (NSData*)test
{
return _test;
}
#end
There is no setTest: mutator method because the property is declared as readonly, so the copy attribute has no effect.
You can implement your own mutator method:
- (void)setTest:(NSData*)newValue
{
_test = [newValue copy];
}
Or, you can have the compiler synthesize a mutator method for you by declaring a read/write property in a private class extension in your implementation file:
// A.m:
#interface A()
#property (nonatomic, copy) NSData* test;
#end
Both cases would allow you to use the test mutator method to copy a value to the _test instance variable:
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
The end result is:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
#end
#interface A()
#property (nonatomic, copy) NSData* test;
#end
#implementation A
- (instancetype)initWithData:(NSData*)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
#end
In addition to what Darren said, the copy attribute describes what semantics the properties setter has. In your initializer, you're not using the setter, you're directly assigning to the instance variable.
It's maybe a bit hard to grok, but the instance variable is not the same thing as the property. It is used to implement the property in this case. But, assigning to the instance variable is not the same as setting the property.
If you want your initializer to also have the semantics that it copies the passed-in data, that's a separate design decision (although a good idea to go with the property's semantics). You could implement that by using a private setter as Darren suggests, but you could also just do:
_test = [data copy];
in the initializer.

Overriding a property in a protocol to make it mutable

I'm having some issues trying to override a property in a protocol to make it mutable.
I have this protocol:
#protocol TheProtocol
#property (nonatomic, readonly) NSString *someString;
#end
And this class:
#interface SuperObject : NSObject <TheProtocol>
#end
Which synthesizes the variable like so:
#implementation SuperObject
#synthesize someString;
-(id)init {
if(self=[super init]) {
someString = [aString copy];
}
return self;
}
#end
and can thus write to it internally
I then have a subclass:
#interface SubObject : SuperObject
#end
#implementation SubObject
#synthesize someString;
- (id)init {
if(self=[super init]) {
NSLog(#"Some string is %#",someString");
someString = [bString copy];
}
return self;
}
In my subclass, trying to assign to someString doesn't work. I tried also synthesizing someString in my subclass but before I try and modify it, when I print out "someString", it prints nil instead of "something"
Answered
I figured out the answer. Ultimately what worked is this:
#interface SuperObject : NSObject <TheProtocol> {
#protected
NSString *someString;
}
#end
What is happening is that when you synthesize in your subclass, that is allocating a different ivar than the one in your super class. Therefore the value when printed is nil. You can refer to the original ivar by doing self.someString.
Hope that helps.

Objective-C Where to define initial Class Properties

just wanted to ask where I define initial class properties?
From other languages I am used to define some standard properties in the head before the content of the class starts.
For example paths to files. Settings and so on.
Where I fill these initial properties with values in Objective-C?
Thanks
Generally it's something like:
MyClass.h:
extern NSString * const staticValue1;
extern NSString * const staticValue2;
#interface MyClass : NSObject
{
NSString *_strval;
int _intval;
float _fltval;
}
#property (retain, nonatomic, readwrite) NSString *strval;
#property (assign, nonatomic, readwrite) int intval;
#property (assign, nonatomic, readwrite) float fltval;
#end
MyClass.m:
NSString * const staticValue1 = #"Something";
NSString * const staticValue2 = #"Something else";
#interface MyClass
#synthesize strval = _strval;
#synthesize intval = _intval;
#synthesize fltval = _fltval;
- (id)init
{
self = [super init];
if (self != nil)
{
[self setStrval:[NSString stringWithFormat:#"This is a %#", #"string"]];
[self setIntval:10];
[self setFltval:123.45f];
}
return self;
}
- (void)dealloc
{
[self setStrval:nil];
[super dealloc];
}
#end
This demonstrates the use of synthesized properties which are being used here to manage the memory of the instance variable _strval, which requires retaining/releasing to avoid memory leaks. Note that [self setStrval] is initialised with an autoreleased object (from [NSString stringWithFormat) and will be retained by the setter method. Alternatively these methods can be called using the following syntax, if you prefer:
self.strval = [NSString stringWithFormat:#"This is a %#", #"string"];
self.intval = 10;
self.fltval = 123.45f;
Maybe some of what you are after can be implemented with class methods.
Class methods are coded with a + (instead of the instance methods' -), and can't refer to instance variables, as they are not associated with any specific instance of the class.
This is a class method to return a default string:
+ (NSString *)myDefaultString
{
return #"Some default value";
}
You call it by simply calling it with the class name at the receiver's place. Imagine you have defined the method in a class called MyClass, the you call it like this:
NSString *str = [MyClass myDefaultString];
You'll notice that there is no alloc/init calls in this.
Public property needs to be define in .h file.
#interface MyClass {
}
#property(nonatomic, reatin) NSString *a;//Define as per needs, then synthesise in .m file
#end
For private property you need define inline category in .m file-
#interface MyClass ()
#property(nonatomic, reatin) NSString *b;//Define as per needs, then synthesise in .m file
#end
#implementation MyClass
#synthesize a = _a;
#synthesize b = _b;
- (void)viewDidLoad {
//You can initialise property here or in init method
self.a = #"Demo1";
self.b = #"Demo2";
}
//Now you can have other code for this class.
#end

Is that the correct way to deal with ivars?

I have read a lot of topics about getters and setters. I know what they are and why are useful. Different source claim's different ways to release the ivars. Here begins my confusion
#interface CoolClass : NSObject
{
NSString *name;
}
#property (nonatomic, copy) NSString *name;
#end
#implementation CoolClass
#synthesize name = _name;
-(id)init
{
if(super = [self super])
{
self.name = #"Jo";
}
return self;
}
-(void)dealloc
{
[self.name release], self.name = nil;
}
#end
Is that the correct way to release/free ivars ?
You'll want to use accessors most of the time, but not in partially constructed states because they can have negative side-effects. Here's how it's done:
- (id)init
{
if((self = [super init])) {
// self.name = #"Jo"; << don't use accessors in initializer
_name = [#"Jo" copy]; << good
}
return self;
}
// added for another variation:
- (id)initWithName:(NSString *)inName
{
if((self = [super init])) {
_name = [inName copy];
}
return self;
}
- (void)dealloc
{
// don't use accessors in dealloc
// don't release the result of a getter (release the result of the retained or copied result)
// [self.name release], self.name = nil;
// instead:
[_name release], _name = nil;
[super dealloc]; << compiler should have warned you about this one
}
Note: In the case of init, the string literal is an immortal and it won't matter if you copy it because the copy just returns itself. My preference is to just 'copy' the immortal for clarity, although it's unnecessary.
Here is what I would advise:
#interface CoolClass : NSObject
#property (nonatomic, copy) NSString *name;
#end
#implementation CoolClass
#synthesize name = _name;
-(id)init
{
if(super = [self super])
{
self.name = #"Jo";
}
return self;
}
-(void)dealloc
{
[_name release];
[super dealloc];
}
#end
Notes:
There is no need to explicitly declare ivars inside { ... } in your header. They will be created automatically when you synthesise your property. Explicit ivars are a legacy concept that are no longer needed since about iOS 3.
You should not use self.name in the dealloc as this calls the getter method, which may do additional work beyond merely fetching the ivar. Normally it's good practice to use the getter method, but in the dealloc you should release the ivar directly
It is good practice to set ivars to nil after releasing them, but again in the dealloc this in not necessary because no code is ever executed after dealloc, so the pointer won't be referenced again.
Normally (outside of the dealloc), if you wish to release an ivar you should set it to nil using the setter like this: self.name = nil; that will automatically release it and set it to nil. This is equivalent to [_name release], _name = nil;
#interface CoolClass : NSObject
{
NSString *name;
}
You declared here an instance variable 'name'; Nowadays there is no need to declare ivars in the header file. Just use properties and make the compiler to synthesize ivar for you.
#property (nonatomic, copy) NSString *name;
Here we have a property declaration that specifies that a copy of the object should be used for assignment and that a previous value is sent a release message.
In implementation you want to synthesize your property:
#synthesize name = _name;
This code tells the compiler to generate a getter and setter for property called 'name' and use instance variable called '_name' to store value. So you have now two ivars - 'name' and '_name'.
That how init method should like like:
-(id)init
{
if(self = [super init])
{
name = #"This is ivar declared between {}";
_name = #"synthesized ivar";
}
return self;
}
And the dealloc:
-(void)dealloc
{
[name release];
[_name release];
[super dealloc];
}

In Objective-C, how do I pass a property as an argument for a function and then call the setter/getter methods?

The code is probably the best way to see what I am trying to do:
AcInfo.h:
#interface AcInfo : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString *registrationNumber;
#end
AcInfo.m:
#implementation AcInfo
#dynamic registrationNumber;
#end
AnotherClass.h:
#interface AnotherClass : NSObject {
}
#property (nonatomic, retain) AcInfo *detailItem;
#property (nonatomic, retain) IBOutlet UITextField *registrationNumberTextField;
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget;
#end
AnotherClass.m:
#import "AcInfo.h"
#implementation AnotherClass
#synthesize detailItem, registrationNumberTextField;
- (void)viewDidLoad
{
[super viewDidLoad];
registrationNumberTextField.text = #"Test";
// I expect this to set detailItem.registrationNumber to the value of
// registrationNumberTextField.text (Test) but it doesn't change anything!
setDetailItemValueFromUIElement:registrationNumberTextField forAcInfoTarget:detailItem.registrationNumber;
}
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget
{
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
// This doesn't do anything when it returns!
(NSString *)acInfoTarget = (UITextField *)uiElement.text
return;
}
}
#end
In short, I want acInfoTarget to call the getter [detailObject registrationNumber] and the setter [detailObject setRegistrationNumber] in the setDetailItemValueFromUIElement: function...
You can set or read properties by name using
// setter
NSString *propertyName = #"myProperty";
[object setValue:newValue forKey:propertyName];
// getter
id value = [object valueForKey:propertyName];
This is slower than using the normal dot notation, though, and it's frequently (though not always) a sign of poorly-designed code.
Also note that id is a pointer type, so you probably don't actually mean "(id*)".
Your code wants to look something like this, I think:
- (void)setDetailItemValueFromUIElement:(id)uiElement forAcInfoTarget:(NSString*)acInfoTarget {
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
NSString *newValue = ((UITextField*)uiElement).text;
[self.detailItem setValue:newValue forKey:acInfoTarget];
}
}
Properties are just syntax sugar for a couple of accessor methods. They are not, in essence, variables so you shouldn't treat them as such. If you want to affect a property, then what you wanting to do is call a method. So you should pass a id and selector parameter and not pointer to a variable type.