Sas*_*ats 6 arguments weak-references automatic-ref-counting swift
有没有办法指定特定方法参数具有弱语义?
详细说明,这是一个按预期工作的Objective-C示例代码:
- (void)runTest {
__block NSObject *object = [NSObject new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self myMethod:object];
});
// to make sure it happens after `myMethod:` call
dispatch_async(dispatch_get_main_queue(), ^{
object = nil;
});
}
- (void)myMethod:(__weak id)arg0 {
NSLog(@"%@", arg0); // <NSObject: 0x7fb0bdb1eaa0>
sleep(1);
NSLog(@"%@", arg0); // nil
}
Run Code Online (Sandbox Code Playgroud)
这是Swift版本,但没有
public func runTest() {
var object: NSObject? = NSObject()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
self.myMethod(object)
}
dispatch_async(dispatch_get_main_queue()) {
object = nil
}
}
private func myMethod(arg0: AnyObject?) {
println("\(arg0)") //Optional(<NSObject: 0x7fc778f26cf0>)
sleep(1)
println("\(arg0)") //Optional(<NSObject: 0x7fc778f26cf0>)
}
Run Code Online (Sandbox Code Playgroud)
我是否正确假设在Swift版本的方法调用之间arg0无法变为零?谢谢!
从Apple Dev.Forums 更新用户指出这sleep不是一个好用的函数,连续调度可能会导致竞争条件.虽然这些可能是合理的问题,但这只是一个示例代码,问题的焦点在于传递弱论点.
Swift没有"弱args"......但这可能只是因为Swift(3.0)args是不可变的(相当于lets)而且weakSwift中的东西需要同时是a var和a Optional.
也就是说,确实有一种非常简单的方法可以实现相当于弱的args - 使用weak varlocal (释放arg-var来释放).这是有效的,因为Swift在当前作用域结束之前不会挂起变量(就像C++一样严格); 但是它会在最后一次使用它后从范围中释放变量(这lldb有时会产生一个PitA,但无论如何).
以下示例在macOS 10.11.6上的Xcode 8.2.1上的Swift 3.0.2中一致地工作:
class Test
{
func runTest() {
var object:NSObject? = NSObject()
myMethod(arg0: object)
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + 1.0,
qos: .userInteractive,
flags: DispatchWorkItemFlags.enforceQoS
){
object = nil
}
}
func myMethod(arg0:AnyObject?) {
weak var arg0Weak = arg0
// `arg0` get “released” at this point. Note: It's essential that you
// don't use `arg0` in the rest of this method; only use `arg0Weak`.
NSLog("\(arg0Weak)"); // Optional(<NSObject: 0x600000000810>)
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + 2.0,
qos: .userInteractive,
flags: DispatchWorkItemFlags.enforceQoS
){
NSLog("\(arg0Weak)"); // nil
}
}
}
Test().runTest()
Run Code Online (Sandbox Code Playgroud)
请注意,如果您在游乐场中尝试此操作,操场将在DispatchQueue火灾前完成执行.让可执行文件无限期运行的最简单方法(我所做的)是创建一个新的Cocoa应用程序并将上面的所有代码粘贴到func applicationDidFinishLaunching(_:Notification) { … } (是的,逐字 - Swift允许嵌套在方法内的类定义).
为了响应线程安全讲座,你已经完成了在你的例子中使用dispatch_async&sleep,以证明弱args确实是真正的交易,这里main.m是你的测试的完整源代码,单线程和无队列:
#import <Foundation/Foundation.h>
@interface Test : NSObject
- (void)runTest;
- (void)myMethod:(__weak id)arg0 callback:(void (^)())callback;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[[Test new] runTest];
}
return 0;
}
@implementation Test
- (void)runTest {
__block NSObject *object = [NSObject new];
[self myMethod:object callback:^{
object = nil;
}];
}
- (void)myMethod:(__weak id)arg0 callback:(void (^)())callback {
NSLog(@"%@", arg0); // <NSObject: 0x100400bc0>
callback();
NSLog(@"%@", arg0); // (null)
}
@end
Run Code Online (Sandbox Code Playgroud)
目前尚无法使用语言语法。
我认为这种解决方法是目前最接近的。
public struct Weak<T> where T: AnyObject {
public weak var object: T?
public init(_ object: T?) {
self.object = object
}
}
func run(_ a: Weak<A>) {
guard let a = a.object else { return }
}
Run Code Online (Sandbox Code Playgroud)
有什么方法可以指定特定方法参数具有弱语义吗?
这不是您的 Objective-C 代码示例所做的。您意外地获得了几乎弱的语义,并且具有真正的弱引用所没有的未定义行为(竞争条件)。
myMethodNSLog可以在任何序列点(第一个语句或第二个语句,甚至在某个地方的中间)向 la-la-land 发送消息NSLog......即使 ARC 没有消除保留,arg0您仍然在与主队列释放竞争或者更糟 - 保留僵尸对象)。
将某物声明为__blockjust 意味着在堆环境中为该块分配一个槽(因为dispatch_async保证让块逃逸,它将从堆栈分配的块提升为堆块,并且该堆块环境中的存储槽之一将是你的__block变量。在 ARC 下,块将自动调用Block_copy,也许更恰当地命名Block_copy_to_heap)。
这意味着该块的两个执行实例将指向同一内存位置。
如果有帮助的话,想象一下这个非常愚蠢的代码,它有一个明显的竞争条件。有 1000 个块同时排队,所有块都试图修改unsafe。我们几乎肯定会执行 if 块内的讨厌语句,因为我们的赋值和比较不是原子的,而且我们正在争夺相同的内存位置。
static volatile size_t unsafe = 0;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(1000, queue, ^(size_t instance) {
unsafe = instance;
if (unsafe != instance) {
FORMAT_ALL_STORAGE();
SEND_RESIGNATION_EMAIL();
WATCH_THE_WORLD_BURN();
}
});
Run Code Online (Sandbox Code Playgroud)
您的 Swift 示例没有相同的问题,因为不修改值的块可能捕获(并保留)该对象,因此看不到其他块的修改。
由闭包的创建者来处理内存管理后果,因此你不能创建一个在闭包中强制不保留循环的 API 合约,除非标记一些东西,在这种情况下@noescapeSwift 不会做任何保留/释放或其他内存管理,因为该块的寿命不会超过当前堆栈帧的寿命。由于显而易见的原因,这排除了异步调度。
如果你想提供一个 API 合约来解决这个问题,你可以让一个类型采用一个协议,protocol Consumer: class { func consume(thing: Type) }然后在你的 API 内部保留对实例的弱引用Consumer。
另一种技术是接受实例函数的柯里化版本并弱捕获self:
protocol Protocol: class { }
typealias FuncType = () -> Void
var _responders = [FuncType]()
func registerResponder<P: Protocol>(responder: P, usingHandler handler: (P) -> () -> Void) {
_responders.append({ [weak responder] in
guard let responder = responder else { return }
handler(responder)()
})
}
class Inst: Protocol {
func myFunc() {
}
}
let inst = Inst()
registerResponder(inst, usingHandler: Inst.myFunc)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5162 次 |
| 最近记录: |