在自动引用计数之前,您可以在Objective-c中进行适当的指针强制转换,以允许您使用bool OSAtomicCompareAndSwapPtr(void*oldValue、void*newValue、void*volatile*theValue);在处理多线程访问时尝试原子交换指针。
在ARC下,这些指针强制转换无效。iOS版的ARC下是否有等效的原子指针交换?我希望如果这种替代方案仍然可用,可以避免更昂贵的锁定。
免责声明:此答案中的代码未经测试!
首先,我想提到的是,大多数指针的使用实际上并不需要比较和交换。C指针的读取和写入本身就是原子的。请参阅此SO答案以了解更多详细信息。ARM也是如此。因此,如果你实现了原子getter和setter,你只需要一个内存屏障来保证其他线程看到完全初始化的对象:
NSObject * global;
NSObject * get() { return global; }
void set(NSObject * value) { OSMemoryBarrier(); global = value; }
现在回到问题上来,因为谁知道呢,比较和交换对象可能有真正的用途。铸造仍然是可能的,你现在只是以不同的方式声明它们:
NSString * a = @"A";
NSObject * b = @"B";
OSAtomicCompareAndSwapPtrBarrier(
(__bridge void *)a,
(__bridge void *)b,
(__bridge void * *)&a);
然而,这段代码有一个问题:字符串@"A"
失去了一个引用,而@"B"
被引用了两次,而ARC并不知道。因此,@"A"
将泄漏,并且程序在离开作用域时可能崩溃,因为@"B"
将被释放两次,而只有1
的保留计数器。
我认为唯一的选择是使用Core Foundation对象。您可以使用NSObject与CFType免费桥接的事实。我找不到任何关于这方面的明确文件,但它是根据常识和实际证据得出的。因此,例如,可以实现一个单例:
CFTypeRef instance;
Thingamabob * getInstance() {
if (!instance) {
CFTypeRef t = (__bridge_retained CFTypeRef)[Thingamabob new];
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, t, &instance)) {
CFRelease(t);
}
}
return (__bridge Thingamabob *)instance;
}
如果满足一个条件,如果你愿意玩游戏,你可能可以很容易地使用它。
创建一个新文件,在构建阶段将其标记为不使用ARC,并将此交换放入一个小的C函数中。在函数的顶部获取对象的retainCount,如果它们相等(并且你有理由相信它们不在自动释放池中),你可以直接交换它们,因为ARC将确保每个对象都有正确的释放。
如果他们不相等,那么,你可以通过改变保留计数来玩游戏。
我发现了这些可比较的类-之前和更新,这有助于说明的更新
之前
#import "SBlockDisposable.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
@interface SBlockDisposable ()
{
void *_block;
}
@end
@implementation SBlockDisposable
- (instancetype)initWithBlock:(void (^)())block
{
self = [super init];
if (self != nil)
{
_block = (__bridge_retained void *)[block copy];
}
return self;
}
- (void)dealloc
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
strongBlock = nil;
}
}
}
}
- (void)dispose
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
((dispatch_block_t)strongBlock)();
strongBlock = nil;
}
}
}
}
@end
后
//
// KAGRACDisposable.m
// ReactiveCocoa
//
// Created by Josh Abernathy on 3/16/12.
// Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
#import "KAGRACDisposable.h"
#import "KAGRACScopedDisposable.h"
#import <stdatomic.h>
@interface KAGRACDisposable () {
// A copied block of type void (^)(void) containing the logic for disposal,
// a pointer to `self` if no logic should be performed upon disposal, or
// NULL if the receiver is already disposed.
//
// This should only be used atomically.
void * volatile _disposeBlock;
}
@end
@implementation KAGRACDisposable
#pragma mark Properties
- (BOOL)isDisposed {
return _disposeBlock == NULL;
}
#pragma mark Lifecycle
- (id)init {
self = [super init];
if (self == nil) return nil;
_disposeBlock = (__bridge void *)self;
atomic_thread_fence(memory_order_seq_cst);
return self;
}
- (id)initWithBlock:(void (^)(void))block {
NSCParameterAssert(block != nil);
self = [super init];
if (self == nil) return nil;
_disposeBlock = (void *)CFBridgingRetain([block copy]);
atomic_thread_fence(memory_order_seq_cst);
return self;
}
+ (instancetype)disposableWithBlock:(void (^)(void))block {
return [[self alloc] initWithBlock:block];
}
- (void)dealloc {
if (_disposeBlock == NULL || _disposeBlock == (__bridge void *)self) return;
CFRelease(_disposeBlock);
_disposeBlock = NULL;
}
#pragma mark Disposal
- (void)dispose {
void (^disposeBlock)(void) = NULL;
while (YES) {
void *blockPtr = _disposeBlock;
if (atomic_compare_exchange_strong((volatile _Atomic(void*)*)&_disposeBlock, &blockPtr, NULL)) {
if (blockPtr != (__bridge void *)self) {
disposeBlock = CFBridgingRelease(blockPtr);
}
break;
}
}
if (disposeBlock != nil) disposeBlock();
}
#pragma mark Scoped Disposables
- (KAGRACScopedDisposable *)asScopedDisposable {
return [KAGRACScopedDisposable scopedDisposableWithDisposable:self];
}
@end