7

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)
Pawan Rai
  • 3,434
  • 4
  • 32
  • 42
Stanislav Pankevich
  • 11,044
  • 8
  • 69
  • 129
  • check this [link1](http://www.raywenderlich.com/2712/properties-tutorial-for-ios) [link2](https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html) – Pawan Rai Feb 15 '14 at 19:01
  • @pawan, I've read both links, I don't think they are relevant to this question. Thanks anyway. – Stanislav Pankevich Feb 15 '14 at 19:47

2 Answers2

12

1. @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];
        }
    }

2. @property (nonatomic, copy) NSNumber *count;

    - (NSNumber *)count {
        return _count;
    }

    - (void)setCount:(NSNumber *)count {
        NSNumber *oldCount = _count;
        _count = [count copy];
        [_oldCount release];
    }

3. @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.

4. @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.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • Could you comment on why your 3. (atomic, retain) is different from what we can see here: http://stackoverflow.com/questions/8382523/setter-and-getter-for-an-atomic-property?lq=1 (also contains retain-autorelease)? Who is wrong then: you or the linked post? – Stanislav Pankevich Feb 15 '14 at 19:09
  • @stanislaw I stand corrected, a `retain`/`autorelease` is indeed needed, but you would also need to wrap the `retain` inside a `synchronized` block. Check my edited answer. – Gabriele Petronella Feb 15 '14 at 19:13
  • @Gabrielle, thanks for the answer. I will wait for a while before accepting your answer as I want to re-check and understand the stuff you've written. – Stanislav Pankevich Feb 15 '14 at 19:26
  • @Stanislaw, you're welcome. It's a rather long answer already, so I didn't comment much on the implementation. If you have specific questions related to my answer, feel free to ask :) – Gabriele Petronella Feb 15 '14 at 19:28
  • @Gabrielly, this article you've linked is very good. Let me share with you the similar article on this topic: [What clang taught us about Objective-C properties](http://www.crashlytics.com/blog/what-clang-taught-us-about-objective-c-properties/) – Stanislav Pankevich Feb 15 '14 at 19:50
  • 1
    The nonatomic retain and copy setters unfortunately have an unnecessary race condition. If, on thread 1, the setter releases `_count`, and on thread 2 the getter accesses `_count` before thread 1 has set `_count = [count retain]`, thread 2 may access a deallocated object. Always store the new value in `_count` before releasing the old value. The real accessor in the Objective-C runtime does it correctly. See `reallySetProperty` in [`objc-accessors.mm`](http://www.opensource.apple.com/source/objc4/objc4-551.1/runtime/Accessors.subproj/objc-accessors.mm). – rob mayoff Feb 15 '14 at 19:54
  • @robmayoff, thanks, very interesting. Gabrielle, would you mind editing your answer to reflect this? – Stanislav Pankevich Feb 15 '14 at 20:04
  • @robmayoff thank you for pointing it out, I updated my answer removing the race condition and also the unnecessary identity check on the `copy` property. – Gabriele Petronella Feb 16 '14 at 14:46
4

While @Gabriele answer is correct, I want to write my own answer containing:

  1. My research in linked topic: Immutable property for mutable ivar using MRC
  2. Comment by @robmayoff
  3. exploration of objc runtime

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:

Community
  • 1
  • 1
Stanislav Pankevich
  • 11,044
  • 8
  • 69
  • 129