使用 Objective-C 块作为 @synchronized 锁
Using Objective-C block as a @synchronized lock
我有一个块 属性 定义如下:
@property (nonatomic, strong) void(^block)(void);
然后我尝试将其用作 @synchronized
锁:
@synchronized (self.block)
{
//doing something
}
编译器不喜欢这样。它说 @synchronized requires an Objective-C object type ('void (^)(void)' invalid)
所以我通过将块转换为 id
来欺骗编译器
@synchronized ((id)self.block)
{
//doing something
}
它似乎有效,但我不确定我做的是否正确。可能有一些我不知道的注意事项。你怎么看待这件事?使用这样的块是否可以接受,或者由于我不知道的某些原因,这是一种不好的做法?
您可以同步 NSObject
和子类。
在某个阶段深入到一个块变成 __NSMallocBlock__
或 __NSStackBlock__
但这不是你应该依赖甚至真正意识到的东西。
我认为您需要有一个单独的对象作为锁。你为什么不做
@property (nonatomic,strong) NSObject * lock;
并在需要同步时同步。此外,您可以在设置块时设置它,例如
-(void)setBlock:(...)block
{
_block = block;
self.lock = NSObject.new;
}
你准备好了。
编辑
看到这个 NSDictionary and Objective-C block quirk - 小心块。
编译器优化了它们的存储方式,这在某些情况下可能会导致麻烦。如果你有在一个块上同步,那么宁愿做类似
的事情
__strong NSObject * p = block;
@synchronize ( p )
{
// ....
}
确保您和编译器就同步的内容达成一致。这里的 __strong
有点矫枉过正,你可以省略它,但只是明确说明你为什么需要它。
我认为,在实践中,做您正在做的事情应该是安全的。 Objective-C 块是 NSObject
的实例(虽然这是一个实现细节),并且您通常可以用一个块做任何您可以用任何其他对象做的事情。
NSStackBlock
s 有时会有些棘手的事情,它表示捕获变量的块,这些变量在第一次复制之前最初驻留在堆栈中。它们很棘手,因为您必须复制它们,而不仅仅是保留它们,以便保留它们供以后使用。但是,在您的情况下,您正在处理存储在 属性 中的块。如果您正确编写代码,则必须在存储之前复制存储在 属性 中的块(因为 属性 可能比堆栈帧长)。 属性 与 copy
合成,或者您通过 getter 和 setter 实现了 属性,在这种情况下,当您将某些内容分配给块类型的实例变量时,ARC 应该复制它。 (如果您使用的是 MRC,那么您必须确保在将其存储在实例变量中之前复制它。)因此,假设它被复制,它必须是一个 NSGlobalBlock
(表示不捕获变量的块)或 NSMallocBlock
(表示捕获已复制变量的块)。这两个都像普通的 Objective-C 对象一样,只要有强引用就一直有效。
我能想到的唯一警告是,如果它是一个 NSGlobalBlock
(即它是一个不捕获变量的块),那么对于该块的所有用途只有一个块对象。因此,在该块的不同实例上进行同步将共享相同的锁,这可能比您预期的更强大。
我有一个块 属性 定义如下:
@property (nonatomic, strong) void(^block)(void);
然后我尝试将其用作 @synchronized
锁:
@synchronized (self.block)
{
//doing something
}
编译器不喜欢这样。它说 @synchronized requires an Objective-C object type ('void (^)(void)' invalid)
所以我通过将块转换为 id
@synchronized ((id)self.block)
{
//doing something
}
它似乎有效,但我不确定我做的是否正确。可能有一些我不知道的注意事项。你怎么看待这件事?使用这样的块是否可以接受,或者由于我不知道的某些原因,这是一种不好的做法?
您可以同步 NSObject
和子类。
在某个阶段深入到一个块变成 __NSMallocBlock__
或 __NSStackBlock__
但这不是你应该依赖甚至真正意识到的东西。
我认为您需要有一个单独的对象作为锁。你为什么不做
@property (nonatomic,strong) NSObject * lock;
并在需要同步时同步。此外,您可以在设置块时设置它,例如
-(void)setBlock:(...)block
{
_block = block;
self.lock = NSObject.new;
}
你准备好了。
编辑
看到这个 NSDictionary and Objective-C block quirk - 小心块。
编译器优化了它们的存储方式,这在某些情况下可能会导致麻烦。如果你有在一个块上同步,那么宁愿做类似
的事情__strong NSObject * p = block;
@synchronize ( p )
{
// ....
}
确保您和编译器就同步的内容达成一致。这里的 __strong
有点矫枉过正,你可以省略它,但只是明确说明你为什么需要它。
我认为,在实践中,做您正在做的事情应该是安全的。 Objective-C 块是 NSObject
的实例(虽然这是一个实现细节),并且您通常可以用一个块做任何您可以用任何其他对象做的事情。
NSStackBlock
s 有时会有些棘手的事情,它表示捕获变量的块,这些变量在第一次复制之前最初驻留在堆栈中。它们很棘手,因为您必须复制它们,而不仅仅是保留它们,以便保留它们供以后使用。但是,在您的情况下,您正在处理存储在 属性 中的块。如果您正确编写代码,则必须在存储之前复制存储在 属性 中的块(因为 属性 可能比堆栈帧长)。 属性 与 copy
合成,或者您通过 getter 和 setter 实现了 属性,在这种情况下,当您将某些内容分配给块类型的实例变量时,ARC 应该复制它。 (如果您使用的是 MRC,那么您必须确保在将其存储在实例变量中之前复制它。)因此,假设它被复制,它必须是一个 NSGlobalBlock
(表示不捕获变量的块)或 NSMallocBlock
(表示捕获已复制变量的块)。这两个都像普通的 Objective-C 对象一样,只要有强引用就一直有效。
我能想到的唯一警告是,如果它是一个 NSGlobalBlock
(即它是一个不捕获变量的块),那么对于该块的所有用途只有一个块对象。因此,在该块的不同实例上进行同步将共享相同的锁,这可能比您预期的更强大。