如何解决在 Swift 中多次过滤 weak
我对 Swift 或 OOP 一无所知,但我必须构建一个基本的 BLE 扫描仪应用程序。我找到了一个例子,我阅读了它,试图从它的立场去理解和发展。
现在,我有一个这样的功能
@objc private func startSearch() {
bluetoothProvider.startScanning()
.filter { [weak self] newPeripheral in
guard let self = self else { return false }
return !self.scannedPeripherals.contains(where: { $0.peripheral.identifier == newPeripheral.peripheral.identifier
})
}
.subscribe(onNext: { [weak self] in self?.scannedPeripherals.append($0) })
.disposed(by: disposeBag)
}
我真的从一周开始什么都没明白,后卫部分......但我想我明白了其他的东西。现在我想至少添加一个其他过滤器,但它看起来不像我可以在教程或示例中找到的任何其他过滤器机制。任何人都可以在这里解释格式或帮助添加另一个过滤器吗?
我试过这些
.filter { [weak self] filteredPeripheral in
guard let self = self else { return false }
return !self.scannedPeripherals.contains(where: { _ in filteredPeripheral.rssi.intValue < 0
})
}
.filter{ [weak self] filteredPeripheral in
guard let self = self else { return false }
return !self.scannedPeripherals.contains(where: { $0.peripheral.identifier.uuidString.contains("0863")
})
}
但看起来没有任何影响。
解决方法
您当然可以将过滤器链接在一起。根据您的评论,我认为您不需要在第二个或第三个过滤器中对 self 进行任何引用:
@objc private func startSearch()
{
// You don't need [weak self]
bluetoothProvider.startScanning()
.filter
{ newPeripheral in
return !self.scannedPeripherals.contains {
$0.peripheral.identifier == newPeripheral.peripheral.identifier
}
}.filter { $0.rssi.intValue >= 0 }
.filter { $0.peripheral.identifier.uuidString.contains("0863") }
}
由于您不是动态组合过滤器,也没有映射到其间的不同类型,因此通过将它们压缩到一个过滤器中,您会获得稍微更好的性能。
@objc private func startSearch()
{
bluetoothProvider.startScanning().filter
{ newPeripheral in
!self.scannedPeripherals.contains {
$0.peripheral.identifier == newPeripheral.peripheral.identifier
}
&& newPeripheral.rssi >= 0
&& peripheral.identifier.uuidString.contains("0863")
}
}
由于您是 Swift 的新手,所以我想提出一个学习闭包的建议。首先编写一个传递给 filter
的命名函数(或其他采用闭包参数的方法)。让它工作。一旦你让命名函数工作,然后把它变成一个闭包。
您甚至可以更进一步,编写一个循环来执行您希望它执行的操作。循环的主体本质上是将进入您的闭包的内容。然后将循环体移动到一个命名函数中,这样你的循环就为每个元素调用它。当它起作用时,将循环更改为对 filter
(或 map
或 forEach
)的调用,并将您的命名函数传递给它。如果可行,请将您的函数更改为闭包。
随着时间的推移,您会对不需要中间折射的瓶盖感到满意。但一开始它很有帮助,因为它从你已经理解的东西开始,并朝着新事物努力。
以防万一你还没有接受它,我还会提到你看到 $0
的地方,这是对传递给闭包的第一个参数的引用,当闭包的定义没有赋值时给它取个名字。在第一个闭包中,忽略不必要的 [weak self]
,从 newPeripheral in
开始。这为该闭包的参数提供了一个名称,因此您可以通过 newPeripheral
引用它,但是当您调用 contains
时,您将传递另一个嵌套在第一个闭包中的闭包。该嵌套闭包没有命名其参数,因此它使用 $0
来引用它。对于接受多个参数的闭包,还可以有 $1
、$2
等...
此外,如果您熟悉 C 中“回调”函数的概念,那么这基本上就是闭包经常用于的目的,除了与 C 函数指针不同,它们不必引用命名函数,但可以动态定义,并且闭包可以从周围范围捕获变量。
weak
虽然您的问题实际上不是关于 weak
和 guard
,但您提到您不理解它们,所以我想我会简单地解释它们。
[weak self]
是“捕获语法” - 基本上它是一种告诉编译器如何将符号从周围上下文导入到您传递给 filter
的闭包中的方式。在本例中,您将 self
捕获为 weak
引用。
要了解什么是 weak
引用,了解引用是什么很有用。引用是 Swift 引用在堆中动态分配的东西的方式。这样,就像 C 中指针的主要用途。所有原始类型,加上 Array
、Dictionary
、String
、Set
和任何 {{1} } 或 struct
您定义的都是 value 类型,因此对它们进行 enum
引用是没有意义的。事实上,对它们的唯一引用是当它们作为 weak
参数传递时。
除非您涉足 inout
类型家族,否则您可能在 Swift 中使用的唯一引用类型是某种 UnsafePointer
。
普通引用,通常称为强引用,它增加了被引用事物的引用计数。当它超出范围时,它会减少引用计数。当引用计数变为 0 时,它所引用的事物将被取消初始化和释放。这是 Swift 内存管理的核心。
但是,在某些情况下,您可以创建强引用循环。想象一个树数据结构,其中孩子们保持对他们父母的引用。在这种情况下,如果根节点超出范围,则树(如果它不仅仅是一个根节点)将永远不会被释放,因为引用计数永远不会变为 0,因为子节点持有对父节点的强引用。
class
引用是解决该问题的方法,因为它们不会增加引用计数,因此在树数据结构中,您希望每个节点都对其子节点具有强引用,但是weak
对其父级的引用。这样,当树的根超出范围时,整个树都会被释放。同样的想法也适用于双向链表。每个节点都有一个针对其下一个节点的强引用,以及针对其前一个节点的 weak
引用(或者反过来,只要只有一个强引用)。
weak
引用使用 weak
语法,因为当您要访问它时,它们引用的对象可能不存在。
强引用循环也可能发生在称为“转义”闭包的闭包类型中。有时您会在带有闭包参数的函数声明中看到 Optional
,通常用于完成处理程序。 “转义”闭包是一个存储起来以备后用的闭包。在这种情况下,闭包内对 @escaping
的任何强引用都可以防止其引用的对象被取消初始化和释放,只要闭包被保存在某处。基本上是内存泄漏。
self
不采用 filter
闭包。它不存储闭包,它在返回之前调用它,然后就完成了。所以它的声明不包含 @escaping
,在这种情况下不需要捕获对 @escaping
的弱引用。
我没有提到 self
引用,但它们有点像隐式解包的 unowned
引用。
weak
guard
基本上是一个倒置的 guard
语句,增加了它的 if
块 必须 else
、return
或调用一个返回 throw
的函数,这是一个函数表明它永远不会返回的方式,要么是因为它终止了程序,就像 Never
那样,要么因为它出于某种原因进入了无限循环。在循环中,fatalError()
的 guard
块也可以是 else
或 break
,而在 continue
语句中,它可以是 switch
或 {{1 }}。
您在评论中提到您已经完成了嵌入式 C。C 程序通常具有“保护”if,以便在错误情况下提前退出,或者在简单的情况下将它们移出主逻辑。
break
基本上
fallthrough
相当于
int find(char x,const char* s)
{
if (s == NULL) return -1; // This is a guard "if" in C
for(int i = 0; s[i] != 0; ++i) {
if (s[i] == x) return i;
}
return -1;
}
认为 guard condition else { return }
是说它的条件必须为真才能到达后面的主要代码,否则控制传递到 if !condition { return }
块,它必须以某种方式离开当前上下文。
通常,您可以轻松地使用其中任何一个,但如果将 guard
或 else
与可选绑定结合使用,则会有所不同。
guard
,
我不是 100% 肯定您在没有更多上下文的情况下尝试做的事情,但我的直觉是您正在尝试过滤掉某些与这些模式不匹配的外围设备。您的方法通常是正确的,但您正在检查已扫描的外围设备,而不是新传入的外围设备。尝试添加此过滤器:
.filter { newPeripheral in
return (newPeripheral.rssi.intValue < 0 &&
newPeripheral.peripheral.identifier.uuidString.contains("0863"))
}
您找到的代码中的第一个过滤器似乎只是检查重复项,这就是它查看 scannedPeripherals
的原因(也是它需要 weak self
说明符的原因)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。