I've started programming on Objective-C language in the middle of 2012 in the time when ARC replaced MRC as a general practice making the latter almost unnecessary to learn.
Now I am trying to understand some basics of MRC to deepen my knowledge of Memory Management in Objective-C.
The thing I am interested in now, is how to write getters/setters
for declared @properties
explicitly, by hands.
By this time the only sane example I found is from "Advanced Memory Management Programming Guide" by Apple:
@interface Counter : NSObject {
NSNumber *_count;
}
@property (nonatomic, retain) NSNumber *count;
@end;
- (NSNumber *)count {
return _count;
}
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
_count = newCount;
}
My guess is that to make the same for (nonatomic, copy) I should write something like:
- (NSNumber *)count {
return _count;
}
- (void)setCount:(NSNumber *)newCount {
[_count release];
_count = [newCount copy];
}
So the question is about the rest of combinations I am not sure about:
I would thankful for someone who could show me examples of how explicit getters/setters should be written for the following @property declarations given Manual Reference Counting (MRC) is used:
1. @property (nonatomic, retain) NSNumber *count;
2. @property (nonatomic, copy) NSNumber *count;
3. @property (atomic, retain) NSNumber *count;
4. @property (assign) NSNumber *count;
5. What else is used often under MRC? (please share, if any other combinations exist)
Usually you want setters/getters to be public, because that's what they are for: giving access to data, you don't want to give others direct access to because you don't want them to mess with your implementation dependent details - that's what encapsulation is about.
Getters and setters can allow different access levels - for example the get may be public, but the set could be protected.
The interface specifies that the property should at least have a public setter. The definition and accessibility of the getter is left to the implementing class. So if the interface contract only needs to write, get can be left open. No need to demand a public getter.
@property (nonatomic, retain) NSNumber *count;
- (NSNumber *)count {
return _count;
}
- (void)setCount:(NSNumber *)count {
if (count != _count) {
NSNumber *oldCount = _count;
// retain before releasing the old one, in order to avoid other threads to
// ever accessing a released object through the `_count` pointer.
_count = [count retain];
// safely release the old one.
[_oldCount release];
}
}
@property (nonatomic, copy) NSNumber *count;
- (NSNumber *)count {
return _count;
}
- (void)setCount:(NSNumber *)count {
NSNumber *oldCount = _count;
_count = [count copy];
[_oldCount release];
}
@property (atomic, retain) NSNumber *count;
- (NSNumber *)count {
@synchronized(self) {
NSNumber *tmpCount = [_count retain];
}
return [tmpCount autorelease];
}
- (void)setCount:(NSNumber *)count {
@synchronized(self) {
if (count != _count) {
[_count release];
_count = [count retain];
}
}
}
Note: the Objective-C 2.0 specification, mentions that locks are used internally, but it doesn't specify exactly how. What you see above, is roughly what an atomic getter/setter would look like, but it might not be accurate.
As you can read here, the retain
/autorelease
dance in the getter is meant to prevent a setter in another thread releasing the value before we can return it.
@property (assign) NSNumber *count;
- (NSNumber *)count {
@synchronized(self) {
NSNumber *tmpCount = _count;
}
return tmpCount;
}
- (void)setCount:(NSNumber *)count {
@synchronized(self) {
_count = count;
}
}
Note: atomic
is the default.
Other possible modifiers for properties are readwrite
/readonly
, but they will just have the effect of synthesizing or not the setter.
While @Gabriele answer is correct, I want to write my own answer containing:
1) @property (nonatomic, retain) NSNumber *count;
- (NSNumber *)count {
return _count;
}
- (void)setCount:(NSNumber *)count {
if (count != _count) {
id oldValue = _count;
_count = [count retain];
[oldValue release];
}
}
2) @property (nonatomic, copy) NSNumber *count;
- (NSNumber *)count {
return _count;
}
- (void)setCount:(NSNumber *)count {
id oldValue = _count;
_count = [count copy]; // retains (+1)
[oldValue release];
}
Note: no need in if (count != _count)
check since (copy) produces copies (objc runtime source also behaves this way).
3) @property (atomic, retain) NSNumber *count;
- (NSNumber *)count {
NSNumber *count;
@synchronized(self) {
count = [_count retain]; // +1
}
return [count autorelease]; // delayed -1
}
- (void)setCount:(NSNumber *)count {
id oldValue;
@synchronized(self) {
oldValue = _count;
_count = [count retain];
}
[oldValue release];
}
4) @property (assign) NSNumber *count;
- (NSNumber *)count {
NSNumber *count;
@synchronized(self) {
count = _count;
}
return count;
}
- (void)setCount:(NSNumber *)count {
@synchronized(self) {
_count = count;
}
}
P.S. Recently I did some research for this back-to-the-past Manual Reference Counting, let me share with you the following links which I found to be the best on this topic:
Advanced Memory Management Programming Guide (this is the MUST)
An In-depth Look At Manual Memory Management In Objective-C (this one too!)
What clang taught us about Objective-C properties
Memory and thread-safe custom property methods
Source code of objc runtime.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With