Stanislav Pankevich
Stanislav Pankevich

Reputation: 11388

Explicit getters/setters for @properties (MRC)

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)

Upvotes: 9

Views: 4084

Answers (2)

Stanislav Pankevich
Stanislav Pankevich

Reputation: 11388

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:

Upvotes: 5

Gabriele Petronella
Gabriele Petronella

Reputation: 108159

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.

Upvotes: 12

Related Questions