Хотя переменные, определенные внутри области функции, не могут быть доступны извне, это не означает, что вы не можете использовать их значения после завершения этой функции. PHP имеет хорошо известное ключевое слово static
, которое широко используется в объектно-ориентированном PHP для определения статических методов и свойств, но следует иметь в виду, что static
также может использоваться внутри функций для определения статических переменных.
Статическая переменная отличается от обычной переменной, определенной в области функции, в случае, если она не потеряет значение, когда выполнение программы выходит из этой области. Давайте рассмотрим следующий пример использования статических переменных:
function countSheep($num) {
static $counter = 0;
$counter += $num;
echo "$counter sheep jumped over fence";
}
countSheep(1);
countSheep(2);
countSheep(3);
Результат:
1 sheep jumped over fence
3 sheep jumped over fence
6 sheep jumped over fence
Если бы мы определили $counter
без static
, то каждый раз эхо-значение будет будет таким же, как параметр $num
, переданный функции. Использование static
позволяет построить этот простой счетчик без дополнительного обхода.
Статическая переменная существует только в области локальной функции. Он не может быть доступен за пределами функции, в которой он был определен. Таким образом, вы можете быть уверены, что он сохранит свое значение без изменений до следующего вызова этой функции.
Статическая переменная может быть определена только как скалярным или как скалярное выражение (с PHP 5.6). Присвоение других ценностей неизбежно приводит к отказу, по крайней мере, на момент написания этой статьи. Тем не менее вы можете сделать это только на следующей строке вашего кода:
function countSheep($num) {
static $counter = 0;
$counter += sqrt($num);//imagine we need to take root of our sheep each time
echo "$counter sheep jumped over fence";
}
Результат:
2 sheep jumped over fence
5 sheep jumped over fence
9 sheep jumped over fence
Статическая функция «разделена» между методами объектов тот же класс. Это легко понять, просмотрев следующий пример:
class SomeClass {
public function foo() {
static $x = 0;
echo ++$x;
}
}
$object1 = new SomeClass;
$object2 = new SomeClass;
$object1->foo(); // 1
$object2->foo(); // 2 oops, $object2 uses the same static $x as $object1
$object1->foo(); // 3 now $object1 increments $x
$object2->foo(); // 4 and now his twin brother
Это работает только с объектами того же класса.
Другим способом сохранения значений между вызовами функций является использование замыканий. Закрытие было введено в PHP 5.3. В двух словах они позволяют ограничить доступ к некоторому набору переменных внутри области функций другой анонимной функцией, которая будет единственным способом доступа к ним. Быть в закрывающих переменных могут имитировать (более или менее успешно) концепции ООП, такие как «константы класса» (если они были переданы в закрытии по значению) или «частные свойства» (если они переданы по ссылке) в структурированном программировании.
Последний фактически позволяет использовать замыкания вместо статических переменных. Что нужно использовать, разработчик всегда должен решить, но следует упомянуть, что статические переменные, безусловно, полезны при работе с рекурсиями и заслуживают того, чтобы их заметили разработчики.
Еще один способ справиться с отсутствием столбца _id в таблице - написать подкласс CursorWrapper, который при необходимости добавит столбец _id.
Это имеет то преимущество, что не требует каких-либо изменений в таблицах или запросов.
Я написал такой класс, и если это интересно, его можно найти в https://github.com/cmgharris/WithIdCursorWrapper
Это ответил , и я хотел бы сделать его более всеобъемлющим здесь.
SimpleCursorAdapter требует, чтобы в результирующем наборе Cursor был включен столбец с именем точно «_id». Не спешите изменять схему, если вы не указали столбец «_id» в своей таблице. SQLite автоматически добавляет скрытый столбец с именем «rowid» для каждой таблицы. Все, что вам нужно сделать, это просто выбрать rowid явно и псевдоним как «_id».
SQLiteDatabase db = mHelper.getReadableDatabase();
Cursor cur = db.rawQuery( "select rowid _id,* from your_table", null);
getItemId
и предоставления моего собственного идентификатора? Может ли это решить проблему?
– BornToCode
6 June 2012 в 10:58
Да, я также изменяю строковый запрос SELECT, чтобы исправить эту проблему.
String query = "SELECT t.*,t.id as _id FROM table t ";
Если вы прочитали документы на sqlite, создание любого столбца типа INTEGER PRIMARY KEY будет внутренне псевдонимом ROWID, поэтому не стоит добавлять псевдоним в каждый SELECT, отклоняясь от любых общих утилит, которые могут воспользоваться преимуществами что-то вроде перечисления столбцов, определяющих таблицу.
http://www.sqlite.org/autoinc.html
Это также более прямолинейно использовать это как ROWID вместо опции AUTOINCREMENT, которая может привести к тому, что _ID может отклоняться от ROWID. Привязывание _ID к ROWID означает, что первичный ключ возвращается из insert / insertOrThrow; если вы пишете ContentProvider, вы можете использовать этот ключ в возвращаемом Uri.
Я вижу, что в документации для CursorAdapter указано:
Курсор должен содержать столбец с именем
_id
или этот класс не будет работать.
SimpleCursorAdapter
является производным классом, поэтому представляется, что это утверждение применяется. Однако заявление технически неверно и несколько вводит в заблуждение новичка. Набор результатов для курсора должен содержать_id
, а не сам курсор. Я уверен, что это понятно для администратора баз данных, потому что эта краткая документация им понятна, но для этих новичков, будучи неполным в заявлении, вызывает путаницу. Курсоры подобны итераторам или указателям, они не содержат ничего, кроме механизма трансверсации данных, они не содержат самих столбцов.Документация Loaders содержит пример, где можно видеть, что
_id
включен в параметр проекции.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader<Cursor> onCreateLoader(int id, Bundle args) { // ... return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }
Что решило мою проблему с этой ошибкой, так это то, что я не включил столбец _id в мой запрос БД. Добавив это, я решил проблему.
Это, вероятно, уже не актуально, но сегодня я столкнулся с одной и той же проблемой. Оказывается, имена столбцов чувствительны к регистру. У меня был столбец _ID, но Android ожидает столбец _id.