文章目录
1.0、Introduction
base::Callback<>和base::Bind()他们组合可以提供一种类型安全的回调方法。可以绑定一些参数生成一个函数调用。这样就可以传递延迟执行的函数参数了。例如,在chromium代码中用于不同线程上调度任务,或者在不同的MessageLoops上调度任务。
一个回调函数没有绑定任何参数的时候(base::Callback<void()>)被封装成了base::Closure。注意,这里与其他环境有所不同,它不保留对闭包的引用。
1.1 OnceCallback<> And RepeatingCallback<>
OnceCallback<>由base::BindOnce()创建,这个回到函数变量是一个仅移动类型并且只能运行一次。默认情况下,这会将绑定参数从内部存储转移到绑定函数上去,因此更加容易跟可移动类型一起使用。回调函数有一个很好的特性是:声明周期非常明确,所以更加容易判断线程之间的调用何时被销毁。首选这个回调,其次RepeatingCallback
base::RepeatingCallback<> 由 base::BindRepeating()创建,显然这个是可以反复调用的,所以也很难判断是否被销毁状态等问题。
1.2 Memory Management And Passing
对于base::{Once,Repeating}Callback 如果所有权被转移了就传值,否则传引用。
// |Foo| just refers to |cb| but doesn't store it nor consume it.
bool Foo(const base::OnceCallback<void(int)>& cb) {
return cb.is_null();
}
// |Bar| takes the ownership of |cb| and stores |cb| into |g_cb|.
base::RepeatingCallback<void(int)> g_cb;
void Bar(base::RepeatingCallback<void(int)> cb) {
g_cb = std::move(cb);
}
// |Baz| takes the ownership of |cb| and consumes |cb| by Run().
void Baz(base::OnceCallback<void(int)> cb) {
std::move(cb).Run(42);
}
// |Qux| takes the ownership of |cb| and transfers ownership to PostTask(),
// which also takes the ownership of |cb|.
void Qux(base::RepeatingCallback<void(int)> cb) {
PostTask(FROM_HERE, base::BindOnce(cb, 42));
PostTask(FROM_HERE, base::BindOnce(std::move(cb), 43));
}
如果不需要保存引用就使用std::move()给这个回调函数传值,否则就直接传递对象。当函数需要独占变量,并且你没有使用std::move(),此时你将会得到compile error。
回调变量使用了Reset()函数就会变成空,通过id_null()来判断回调的变量是否为空。
2.0 Quick reference for basic stuff
2.1 Binding A Bare Function
example 1:
int Return5() { return 5; }
base::OnceCallback<int()> func_cb = base::BindOnce(&Return5);
LOG(INFO) << std::move(func_cb).Run(); // Prints 5.
example 2:
int Return5() { return 5; }
base::RepeatingCallback<int()> func_cb = base::BindRepeating(&Return5);
LOG(INFO) << func_cb.Run(); // Prints 5.
2.2 Binding A Captureless Lambda
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.
2.3 Binding A Class Method
第一个参数是函数,第二个参数是对应方法的实例
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.
默认情况下,对象必须支持引用计数,否则你将会得到一个compile error。如果你是在线程间使用,必须要保证引用计数线程安全。如果你不想使用引用计数请见下文的“Advanced binding of member function”
2.4 Running A Callback
回调函数Callback可以调用Run()函数,他具有和回调函数一样的参数。注意base::OnceCallback::Run() 是使用回调对象,并且只能使用他的右值进行调用。
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");
}
RepeatingCallbacks 可以运行多次。除了使用base::Passed,参见下文。
void DoSomething(const base::RepeatingCallback<double(double)>& callback) {
double myresult = callback.Run(3.14159);
myresult += callback.Run(2.71828);
}
注意RepeatingCallbacks有下面这个问题,但是OnceCallback没有,因为OnceCallback永远都是使用std::move来执行Run()函数:
如果运行回调函数导致自身的销毁,那么需要再回调之前,先std::move,如下:
void Foo::RunCallback() {
std::move(&foo_deleter_callback_).Run();
}
2.5 Creating a Callback That Does Nothing
有时候你出发回调但是不需要做任何事情(test code 或者 只是触发一个事件),此时就可以传一个默认构造的相同类型过去
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()!
默认构造Callback为空,所以不能使用Run(),可以使用base::DoNothing()
MyFunction(base::DoNothing()); // Can be Run(), will no-op
base::DoNothing() 可以为任何返回void的OnceCallback和RepeatingCallback来使用。如果尝试给bind参数,这个将导致base::DoNothing()失效。
通常,将参数绑定到DoNothing()的惟一原因是管理对象的生存期,在这种情况下,应该尽量使用DeleteSoon()、ReleaseSoon()或RefCountedDeleteOnSequence之类的习惯用法。
如果你真的需要绑定一个参数到DoNothing(),或者你需要显式地创建一个回调对象(因为通过操作符()()的隐式转换不会编译),你可以直接实例化:
// 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);
2.6 Passing Input Parameters
Passing Unbound Input Parameters
注意绑定参数和不绑定参数的区别
void MyFunc(int i, const std::string& str) {}
base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
cb.Run(23, "hello, world");
Passing Bound Input Parameters
void MyFunc(int i, const std::string& str) {}
base::Callback<void()> cb = base::Bind(&MyFunc, 23, "hello world");
cb.Run();
一个回调没有绑定参数的时候(base::Callback<void()>)称为 base::Closure,有下面的使用方式:
base::Closure cb = base::Bind(&MyFunc, 23, "hello world");
或者
base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
2.7 Partial Binding Of Parameters (Currying)
当使用Callback的时候可以指定部分参数,在执行回调的时候再指定剩下的参数。
void ReadIntFromFile(const std::string& filename,
base::OnceCallback<void(int)> on_read);
void DisplayIntWithPrefix(const std::string& prefix, int result) {
LOG(INFO) << prefix << result;
}
void AnotherFunc(const std::string& file) {
ReadIntFromFile(file, base::BindOnce(&DisplayIntWithPrefix, "MyPrefix: "));
};
这个技术叫做curry,这个应该使用它来代替包含绑定参数的适配器类。注意上面代码有一个参数"MyPrefix: "为const char *,但是对应的bind是const std::string&,这里是进行了类型转换。
2.8 Avoiding Copies With Callback Parameters
避免使用回调参数的副本。base::BindOnce() 一般使用移动构造。
std::vector<int> v = {1, 2, 3};
// |v| is moved into the internal storage without copy.
base::Bind(&Foo, std::move(v));
// The vector is moved into the internal storage without copy.
base::Bind(&Foo, std::vector<int>({1, 2, 3}));
相反的,base::BindRepeating()只有在绑定了base::Passed()的情况下才能使用std::move移动目标。不建议使用base::BindRepeating()和base::Passed()来达到base::BindOnce()的效果,直接使用base::BindOnce()更好。避免同时使用base::Passed()和base::BindOnce(),因为std::move()已经做了相同的事情了。
void Foo(std::unique_ptr<int>) {}
auto p = std::make_unique<int>(42);
// |p| is moved into the internal storage of Bind(), and moved out to |Foo|.
base::BindOnce(&Foo, std::move(p));
base::BindRepeating(&Foo, base::Passed(&p)); // Ok, but subtle.
base::BindRepeating(&Foo, base::Passed(std::move(p))); // Ok, but subtle.
3.0 Quick reference for advanced binding
3.1 Binding A Class Method With Weak Pointers
如果一个类里面有成员base::WeakPtr weak_this_,可以这样使用
base::Bind(&MyClass::Foo, weak_this_);
如果对象被销毁了callback就不能被Run()了。
注意,绑定到base::WeakPtrs的类方法回调只能在销毁对象的同一序列上运行,因为否则回调的执行可能与对象的删除竞争。
要使用base::WeakPtr和base::Bind(), MyClass通常看起来像:
class MyClass {
public:
MyClass() {
weak_this_ = weak_factory_.GetWeakPtr();
}
private:
base::WeakPtr<MyClass> weak_this_;
// MyClass member variables go here.
base::WeakPtrFactory<MyClass> weak_factory_{this};
};
weak_factory_是MyClass中的最后一个成员变量,因此首先销毁它。这如果任何绑定到weak_this_的类方法在销毁期间调用Run(),那么它们实际上不会被执行。
如果MyClass只调用base::Bind()相同的执行回调,那么在调用base::Bind()时调用weak_factory_.GetWeakPtr()通常是安全的,而不是在构造期间使用单独的weak_this_。
3.2 Binding A Class Method With Manual Lifetime Management
base::Bind(&MyClass::Foo, base::Unretained(this));
这样就禁止了这个对象的所有的生命周期的管理,你有必要知道你调用的过程中这个对象一直存在。
3.3 Binding A Class Method And Having The Callback Own The Class
MyClass* myclass = new MyClass;
base::Bind(&MyClass::Foo, base::Owned(myclass));
这个对象在callback被销毁的时候删除,即使没有调用Run()成功,就像是在关机的时候发送了一个任务到线程池一样。
智能指针也支持这样子的情况
std::unique_ptr<MyClass> myclass(new MyClass);
base::Bind(&MyClass::Foo, std::move(myclass));
3.4 Ignoring Return Values
有时候你调用一个callback,但是不需要返回的值,可以这样使用
int DoSomething(int arg) { cout << arg << endl; }
base::Callback<void(int)> cb =
base::Bind(IgnoreResult(&DoSomething));
4.0 Quick reference for binding parameters to Bind()
传递参数给函数是通过用base::Bind()。一个callback没有任何参数的时候叫做base::Closure(),他和base::Callback<void()>是一样的东西。
4.1 Passing Parameters Owned By The Callback
void Foo(int* arg) { cout << *arg << endl; }
int* pn = new int(1);
base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));
当callback被销毁的时候,即使没有run,参数也会被删除的。
4.2 Passing Parameters As A unique_ptr
void TakesOwnership(std::unique_ptr<Foo> arg) {}
auto f = std::make_unique<Foo>();
// f becomes null during the following call.
base::OnceClosure cb = base::BindOnce(&TakesOwnership, std::move(f));
参数的所有权是跟随callback的,会将所有权传递给回调函数的。所以这个只能运行一次,并且如果回调函数没有被运行,它还是会在callback被销毁的时候删除掉的。
4.3 Passing Parameters As A scoped_refptr
void TakesOneRef(scoped_refptr<Foo> arg) {}
scoped_refptr<Foo> f(new Foo);
base::Closure cb = base::Bind(&TakesOneRef, f);
This should “just work.” 这个Closure在它的生命周期内会一直持有它的引用,
void DontTakeRef(Foo* arg) {}
scoped_refptr<Foo> f(new Foo);
base::Closure cb = base::Bind(&DontTakeRef, base::RetainedRef(f));
base::RetainedRed在callback执行Run()的时候回持有对象的引用,并且传递一个原始指针( a raw pointer)给对象。
4.4 Passing Parameters By Reference
引用将会被copy,除非使用std::ref 和 std::cref,Example:
void foo(const int& arg) { printf("%d %p\n", arg, &arg); }
int n = 1;
base::Closure has_copy = base::Bind(&foo, n);
base::Closure has_ref = base::Bind(&foo, std::cref(n));
n = 2;
foo(n); // Prints "2 0xaaaaaaaaaaaa"
has_copy.Run(); // Prints "1 0xbbbbbbbbbbbb"
has_ref.Run(); // Prints "2 0xaaaaaaaaaaaa"
一般来讲,在Closure中参数都是在copy。值得注意的是:std::ref and std::cref存储的是一个reference or const reference. 这个意味着你需要保证在callback之外他们依然在生命周期之内。