@properties的显式getter/setter方法(MRC)

7

我在2012年中期开始使用Objective-C语言进行编程,当时ARC取代MRC成为常规实践,使得后者几乎无需学习。

现在我正在尝试了解一些MRC的基础知识,以加深我对Objective-C内存管理的理解。

我现在感兴趣的是如何手动为声明的属性写入getter/setter。

到目前为止,我找到的唯一合理的例子来自苹果公司的"高级内存管理编程指南":

@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;
}

我的猜测是,要对 (nonatomic, copy) 进行相同的操作,我应该写出类似以下的代码:

- (NSNumber *)count {
    return _count;
}

- (void)setCount:(NSNumber *)newCount {
    [_count release];

    _count = [newCount copy];
}

所以问题在于其余的组合,我不确定:

如果使用手动引用计数(MRC),我希望有人能够示范如何编写以下@property声明的显式getter / setter的示例:

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)

查看此链接 link1 link2 - Pawan Rai
@pawan,我已经阅读了两个链接,我认为它们与这个问题无关。不管怎样,谢谢。 - Stanislav Pankevich
2个回答

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];
            }
        }
    }
注意:Objective-C 2.0规范提到锁定在内部使用,但未具体说明如何使用。上面所看到的大致是原子getter/setter的样子,但可能不准确。
正如您可以在这里所读到的那样,getter中的retain/autorelease操作旨在防止另一个线程中的setter在我们返回值之前释放该值。

4. @property (assign) NSNumber *count;

    - (NSNumber *)count {
        @synchronized(self) {
            NSNumber *tmpCount = _count;
        }
        return tmpCount;
    }

    - (void)setCount:(NSNumber *)count {
        @synchronized(self) {
            _count = count;
        }
    }

注意:atomic 是默认值。

属性的其他可能修饰符是 readwrite/readonly,但它们只会合成或不合成setter方法。


你能否评论一下为什么你的第3点(atomic,retain)与我们在这里看到的不同:https://dev59.com/Q2sy5IYBdhLWcg3wsQFj?lq=1(还包含retain-autorelease)?那么谁错了:你还是链接的帖子? - Stanislav Pankevich
@stanislaw 我承认我错了,确实需要使用 retain/autorelease,但你还需要将 retain 包裹在一个 synchronized 块中。请查看我编辑过的答案。 - Gabriele Petronella
@Gabrielle,谢谢你的回答。我会等一段时间再接受你的答案,因为我想重新检查和理解你写的东西。 - Stanislav Pankevich
@Gabrielly,你分享的这篇文章非常好。让我和你分享一下关于这个主题的类似文章:Clang教我们有关Objective-C属性的知识 - Stanislav Pankevich
1
非原子性的保留和复制的setter方法存在不必要的竞态条件。如果在线程1中,setter方法释放了"_count",并且在线程2中getter方法在线程1设置"_count = [count retain]"之前访问了"_count",那么线程2可能会访问一个已释放的对象。在释放旧值之前,总是将新值存储在"_count"中。Objective-C运行时中的真实访问器已经正确地处理了这一问题。请参见objc-accessors.mm中的reallySetProperty - rob mayoff
显示剩余3条评论

4

尽管@Gabriele的回答是正确的,但我想写一个自己的回答,其中包括:

  1. 我在相关领域的研究:在MRC中使用不可变属性来访问可变的ivar
  2. @robmayoff的评论
  3. objc运行时的探索

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];
}

注意:不需要进行 if (count != _count) 检查,因为(copy)会生成拷贝(Objective-C运行时源代码也是这样处理的)。

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. 最近我对这个回到过去的手动引用计数做了一些研究,让我与您分享以下链接,我认为这是关于此主题的最佳资料:


1
不错的研究工作。我更新了我的答案以反映你最新的发现。我也认为你在第三点中有一个错误。实现正在执行“复制”,而属性被声明为“保留”。 - Gabriele Petronella
谢谢,我已经修复了第三个问题! - Stanislav Pankevich
对不起,我拼错了你的名字,Gabriele! - Stanislav Pankevich
哈哈,没问题。这种情况经常发生,概率非常接近于1 ;) - Gabriele Petronella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接