简介
模板化的base::Callback<>类是一个广义函数对象。与base/bind.h中的base::Bind()函数一起,它们为执行函数的Partial application提供了一种类型安全的方法。
Partial application(或“currying”)是一个绑定函数参数的子集,以生成另一个使用较少参数的函数的过程。这可以用来传递延迟执行的单元,就像在其他语言中使用词法闭包一样。例如,在Chromium代码中使用它在不同的MessageLoop上调度任务。
没有unbound输入参数的回调(base::Callback<void()>)称为base::Closure。请注意,这与其他语言所指的闭包不同——它不保留对其封闭环境的引用。
OnceCallback<>和RepeatingCallback<>
base::OnceCallback<>和base::RepeatingCallback<>是下一代回调类,正在开发中。
base::OnceCallback<>由base::BindOnce()创建。这是一个只移动类型的回调变量,只能运行一次。默认情况下,这会将绑定参数从其内部存储移出到绑定函数,因此更易于与可移动类型一起使用。这应该是首选的回调类型:因为回调的生存期是明确的,所以推理线程之间传递的回调何时被销毁比较简单。
base::RepeatingCallback<>由base::BindRepeating()创建。这是一个可复制的回调变量,可以多次运行。它使用内部引用计数来使副本变得便宜。然而,由于所有权是共享的,所以很难推断回调和绑定状态何时被破坏,特别是当回调在线程之间传递时。
旧版本的base::Callback<>现在的别名为base::RepeatingCallback<>。在新代码中,尽可能选择base::OnceCallback<>,无法使用时才应使用base::RepeatingCallback<>。迁移完成后,base::RepeatingCallback<>别名将被删除,base::OnceCallback<>将重命名为base::Callback<>,以强调首选。
base::RepeatingCallback<>可以通过隐式转换为base::OnceCallback<>。
内存管理和传递
如果所有权转移,则按值传递base::Callback对象;否则,按常量引用传递。
cpp
// |Foo| 只引用 |cb| 但不储存或消耗它。
bool Foo(const base::OnceCallback<void(int)>& cb) {
return cb.is_null();
}
// |Bar| 获取 |cb| 的控制权,存储 |cb| 到 |g_cb|。
base::OnceCallback<void(int)> g_cb;
void Bar(base::OnceCallback<void(int)> cb) {
g_cb = std::move(cb);
}
// |Baz| 获取 |cb| 的控制权并通过 Run() 来消耗 |cb|。
void Baz(base::OnceCallback<void(int)> cb) {
std::move(cb).Run(42);
}
// |Qux| 获取 |cb| 的控制权并转移控制权给 PostTask(),
// PostTask也获取 |cb| 的控制权。
void Qux(base::OnceCallback<void(int)> cb) {
PostTask(FROM_HERE,
base::BindOnce(std::move(cb), 42));
}
当你将base::Callback对象传递给函数参数时,如果不需要保留对它的引用,请使用std::move(),否则直接传递该对象。
当函数需要独占所有权并且你没有按move传递回调时,你可能会看到编译错误。注意,被移动的base::Callback变为NULL,就好像调用了它的reset()方法,并且它的is_null()方法将返回true。
基础资料快速参考
绑定裸函数
```cpp
int Return5() { return 5; }
base::OnceCallback<int()> func_cb = base::BindOnce(&Return5);
LOG(INFO) << std::move(func_cb).Run(); // Prints 5.
```
```cpp
int Return5() { return 5; }
base::RepeatingCallback<int()> func_cb = base::BindRepeating(&Return5);
LOG(INFO) << func_cb.Run(); // Prints 5.
```
绑定无捕获Lambda
```cpp
base::Callback<int()> lambda_cb = base::Bind([] { return 4; });
LOG(INFO) << lambda_cb.Run(); // Print 4.
base::OnceCallback<int()> lambda_cb2 = base::BindOnce([] { return 3; });
LOG(INFO) << std::move(lambda_cb2).Run(); // Print 3.
```
绑定有捕获的Lambda(测试中)
在编写测试时,捕获需要在回调中修改的参数通常很有用。
``` cpp
#include "base/test/bind_test_util.h"
int i = 2;
base::Callback<void()> lambda_cb = base::BindLambdaForTesting([&]() { i++; });
lambda_cb.Run();
LOG(INFO) << i; // Print 3;
```
绑定一个类方法
要绑定的第一个参数是要调用的成员函数,第二个参数是要调用它的对象。
```cpp
class Ref : public base::RefCountedThreadSafe<Ref> {
public:
int Foo() { return 3; }
};
scoped_refptr<Ref> ref = new Ref();
base::Callback<void()> ref_cb = base::Bind(&Ref::Foo, ref);
LOG(INFO) << ref_cb.Run(); // Prints out 3.
```
默认情况下,对象必须支持RefCounted,否则将出现编译器错误。如果要在线程之间传递,请确保它是RefCountedThreadSafe。如果不想使用引用计数,请参阅下面的“成员函数的高级绑定”。
运行回调
回调可以使用其`Run`方法运行,该方法与回调的模板参数具有相同的Signature。注意,base::OnceCallback::Run消耗回调对象,只能在回调rvalue上调用。
```cpp
void DoSomething(const base::Callback<void(int, std::string)>& callback) {
callback.Run(5, "hello");
}
void DoSomethingOther(base::OnceCallback<void(int, std::string)> callback) {
std::move(callback).Run(5, "hello");
}
```
可以多次运行RepeatingCallback(运行时不会删除或标记它们)。但是,这样就不能使用base::Passed了(见下文)。
```cpp
void DoSomething(const base::RepeatingCallback<double(double)>& callback) {
double myresult = callback.Run(3.14159);
myresult += callback.Run(2.71828);
}
```
如果运行回调可能导致其自身销毁(例如,如果回调接收者删除了回调是其成员的对象),则应该在可以安全调用回调之前移动回调。(请注意,这只是RepeatingCallback的问题,因为它总是必须移动OnceCallback才能执行。)
```cpp
void Foo::RunCallback() {
std::move(&foo_deleter_callback_).Run();
}
```
创建不做任何事情的回调
有时,您需要一个在运行时什么都不做的回调(例如,不关心被通知某些类型的事件的测试代码)。传递一个默认构造的正确类型的回调可能很有诱惑力:
```cpp
using MyCallback = base::OnceCallback<void(bool arg)>;
void MyFunction(MyCallback callback) {
std::move(callback).Run(true); // Uh oh...
}
...
MyFunction(MyCallback()); // ...this will crash when Run()!
```
默认构造的回调为null,因此无法Run()。取而代之的是使用base::DoNothing():
```cpp
...
MyFunction(base::DoNothing()); // Can be Run(), will no-op
```
返回void的任何OnceCallback或RepeatingCallback都可以传递base::DoNothing()。
在实现方面,base::DoNothing()实际上是一个从operator()产生回调的函数。这使得它在尝试将其他参数绑定到它时不可用。通常,将参数绑定到DoNothing()的唯一原因是管理对象生存期,在这种情况下,您应该努力使用DeleteSoon()、ReleaseSoon()或RefCountedDeleteOnSequence这样的词语。如果确实需要将参数绑定到DoNothing(),或者如果需要显式创建回调对象(因为通过operator()进行的隐式转换不会编译),则可以直接实例化:
```cpp
// Binds |foo_ptr| to a no-op OnceCallback takes a scoped_refptr<Foo>.
// ANTIPATTERN WARNING: This should likely be changed to ReleaseSoon()!
base::Bind(base::DoNothing::Once<scoped_refptr<Foo>>(), foo_ptr);
```