1. Что такое функция обратного вызова?
Функции обратного вызова звучат внушительнее обычных функций уже из-за своего названия. Так что же такое функция обратного вызова? Простите, что я прочитал не так много книг; я не встречал определения функции обратного вызова ни в одной книге. Я искал в Baidu и нашёл множество разнообразных мнений. Большая часть объясняет это на таком примере: человек A приходит в магазин к человеку B что-то купить, но товара как раз нет в наличии. Человек A оставляет свой номер человеку B, и человек B уведомляет человека A, когда товар появляется в наличии. Это заставляет людей думать скорее об асинхронных операциях, чем об обратных вызовах. Ещё мне запомнились две английские фразы: 1) If you call me, I will call you back; 2) Don't call me, I will call you. Кажется, в них есть смысл, но если подумать, разве обычные функции не могут сделать то же самое? Поэтому я считаю, что эти утверждения не совсем уместны, поскольку они не выражают особенности функций обратного вызова, то есть не показывают их отличие от обычных функций. Однако, на мой взгляд, объяснение в Baidu Baike довольно хорошее (хотя я часто жалуюсь на Baidu Search...): функция обратного вызова — это функция, вызываемая через указатель на функцию. Если вы передаёте указатель (адрес) функции в качестве параметра другой функции, и когда этот указатель используется для вызова функции, на которую он указывает, мы говорим, что это функция обратного вызова.
Позвольте мне сначала поделиться своим мнением. Мы можем разобрать этот термин буквально. Для «callback function» (функция обратного вызова) в китайском его фактически можно понять двумя способами: 1) функция, которую вызывают обратно; 2) функция, которая разворачивается, чтобы выполнить действие вызова. Так что же это за «разворот для вызова»?
Сначала посмотрим на анализ обратных вызовов в Википедии:В компьютерном программировании обратный вызов — это любой исполняемый код, который передаётся в качестве аргумента другому коду и, как ожидается, будет вызван обратно (выполнен) этим кодом в определённый момент. Это выполнение может быть немедленным, как при синхронном обратном вызове, или может произойти позже, как при асинхронном обратном вызове. Иными словами, передача исполняемого кода другому коду в виде параметра, при которой этот код будет вызван и выполнен в некоторый момент, называется обратным вызовом. Если код выполняется сразу, это называется синхронным обратным вызовом; если он выполняется позже, это называется асинхронным обратным вызовом. Здесь мы не будем обсуждать синхронность и асинхронность; пожалуйста, обращайтесь к соответствующим материалам.
Теперь посмотрим на краткое и чёткое утверждение эксперта на Stack Overflow:«Обратный вызов» — это любая функция, которую вызывает другая функция, принимающая первую функцию в качестве параметра. Иными словами, когда функция F1 вызывает функцию F2, функция F1 передаёт указатель на другую функцию F3 в функцию F2 в качестве параметра. Во время выполнения функции F2 функция F2 вызывает функцию F3. Это действие называется обратным вызовом, а функция F3, которая сначала была передана как указатель, а затем вызвана обратно, является функцией обратного вызова. Теперь вы должны понимать определение функции обратного вызова, верно?
2. Зачем использовать функции обратного вызова?
Многие друзья могут задаться вопросом: почему бы просто не написать имя функции прямо в месте обратного вызова, как при обычных вызовах функций? Разве это не сработало бы? Почему мы обязательно должны использовать функции обратного вызова? Хорошо, что есть такая мысль, потому что многие примеры, объясняющие функции обратного вызова, найденные в интернете, на самом деле можно полностью реализовать с помощью обычных вызовов функций. Чтобы ответить на этот вопрос, давайте сначала поймём преимущества и назначение функций обратного вызова, а это — развязывание зависимостей. Да, ответ именно так прост. Именно из-за этой характеристики обычные функции не могут заменить функции обратного вызова. Так что, на мой взгляд, это самая большая особенность функций обратного вызова. Давайте посмотрим на изображение в Википедии, которое, как мне кажется, очень хорошо нарисовано.

Ниже приведён неполный фрагмент кода на C, иллюстрирующий смысл приведённой выше диаграммы:
Пример
#include<softwareLib.h> // Подключите заголовочный файл библиотеки Software, в которой находится Library Function
int Callback() // Функция обратного вызова
{
// TODO
return 0;
}
int main() // Главная программа
{
// TODO
Library(Callback);
// TODO
return 0;
}
На первый взгляд callback кажется просто вызовом между функциями, ничем не отличающимся от обычных вызовов функций. Но при более внимательном рассмотрении можно обнаружить ключевое различие: в callback главная программа передаёт callback-функцию в библиотечную функцию как параметр. Таким образом, достаточно изменить параметры, передаваемые в библиотечную функцию, и мы сможем реализовать разную функциональность. Разве это не ощущается очень гибким? И при этом совсем не нужно изменять реализацию библиотечной функции. Это и есть развязка (decoupling). Посмотрите внимательнее: главная функция и callback-функция находятся на одном уровне, а библиотечная функция — на другом. Подумайте: если библиотечная функция нам не видна, мы не можем изменить её реализацию, а значит, не можем заставить библиотечную функцию вызывать обычные функции, изменяя саму библиотечную функцию. Тогда мы можем добиться этого, только передавая разные callback-функции. Это также распространённая ситуация в повседневной работе. Теперь, если сопоставить main(), Library() и Callback() обратно с более ранними функциями F1, F2 и F3, не становится ли яснее?
Теперь, когда вы понимаете особенности callback-функций, можете примерно определить, когда их следует использовать? Именно так, callback-функции можно использовать во многих местах для замены обычных вызовов функций, но, на мой взгляд, callback-функции следует использовать, когда нужно уменьшить связанность.
3. Как использовать callback-функции?
Теперь, когда вы знаете, что такое callback-функция, и понимаете её особенности, как её использовать? Давайте посмотрим на простой исполняемый код синхронной callback-функции ниже.
Пример
int Callback_1() // Функция обратного вызова 1
{
printf("Привет, это Callback_1 ");
return 0;
}
int Callback_2() // Функция обратного вызова 2
{
printf("Привет, это Callback_2 ");
return 0;
}
int Callback_3() // Функция обратного вызова 3
{
printf("Привет, это Callback_3 ");
return 0;
}
int Handle(int (*Callback)())
{
printf("Вход в функцию Handle. ");
Обратный вызов();
printf("Выход из функции Handle. ");
}
int main()
{
printf("Вход в главную функцию. ");
Handle(Callback_1);
Handle(Callback_2);
Handle(Callback_3);
printf("Выход из главной функции. ");
return 0;
}
Вывод:
Entering Main Function. Entering Handle Function. Hello, this is Callback_1 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_2 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_3 Leaving Handle Function. Leaving Main Function.
Как видите, параметр в функции Handle() — это указатель. При вызове функции Handle() в функции main() ей передаются имена функций Callback_1()/Callback_2()/Callback_3(). В этот момент имя функции — это указатель на соответствующую функцию. Иными словами, функция обратного вызова — это фактически использование указателей на функции. Теперь прочитайте это предложение снова: «callback» — это любая функция, которая вызывается другой функцией, принимающей первую функцию в качестве параметра. Разве теперь не яснее?
4. Как использовать функции обратного вызова с параметрами?
Внимательные читатели могли заметить, что функции обратного вызова в предыдущих примерах не имеют параметров. Так можем ли мы вызывать функции обратного вызова, у которых есть параметры? Ответ — да. Тогда как мы их вызываем? Мы можем просто немного изменить приведённый выше пример:
Пример
int Callback_1(int x) // Функция обратного вызова 1
{
printf("Привет, это Callback_1: x = %d ", x);
return 0;
}
int Callback_2(int x) // Функция обратного вызова 2
{
printf("Привет, это Callback_2: x = %d ", x);
return 0;
}
int Callback_3(int x) // Функция обратного вызова 3
{
printf("Привет, это Callback_3: x = %d ", x);
return 0;
}
int Handle(int y, int (*Callback)(int))
{
printf("Вход в функцию Handle. ");
Обратный вызов(y);
printf("Выход из функции Handle. ");
}
int main()
{
int a = 2;
int b = 4;
int c = 6;
printf("Вход в главную функцию. ");
Handle(a, Callback_1);
Handle(b, Callback_2);
Handle(c, Callback_3);
printf("Выход из главной функции. ");
return 0;
}
Вывод:
Entering Main Function. Entering Handle Function. Hello, this is Callback_1: x = 2 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_2: x = 4 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_3: x = 6 Leaving Handle Function. Leaving Main Function.
Как видите, всё не так просто, как прямое изменение int Handle(int (*Callback)()) на int Handle(int (*Callback)(int)). Вместо этого нужно добавить дополнительный параметр для хранения значения параметра функции обратного вызова, как параметр y в int Handle(int y, int (*Callback)(int)) здесь. Аналогично можно использовать функции обратного вызова с несколькими параметрами.
URL оригинальной статьи: https://www.cnblogs.com/jiangzhaowei/p/9129105.html