在启用ARC的iOS上使用OSAtomicCompareAndSwapPtr



在自动引用计数之前,您可以在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

相关内容

  • 没有找到相关文章

最新更新