Один из наиболее распространенных вопросов, которые я вижу на форумах и получаю от коллег, — как отлаживать ошибку 1009, также известную как «ошибка ссылки на нулевой объект». Или, как я ее называю, «досадная ошибка москита из ада». вверх, и, к сожалению, сама ошибка не содержит много информации об источнике ошибки. В этом кратком совете мы рассмотрим некоторые шаги, которые вы можете предпринять, чтобы выследить этого комара и хорошо его раздавить.
Вступление
Этот фрагмент является первым продолжением более общего руководства «Исправление ошибок в AS3» . Если вы хотите лучше понять некоторые приемы, описанные в этом совете, вы можете сначала прочитать их полностью.
Шаг 1: понять ошибку
Жаль, что Adobe не предоставляет (или не может) предоставить дополнительную информацию о причинах этой ошибки. Прежде всего, это довольно тупо сформулировано (как и все их ошибки, но это больше, чем большинство):
Ошибка типа: ошибка № 1009: невозможно получить доступ к свойству или методу ссылки на пустой объект
Давайте попробуем изложить это в повседневных условиях. Ошибка 1009 означает, что вы пытались что-то сделать с переменной, которая, как вы предполагаете, имеет значение, но на самом деле это не так. Flash не нравится это. Тебе это тоже не понравится; представьте, что у вас был стакан, который, как вы предполагали, был полон вкусного напитка по вашему выбору, но на самом деле был пуст. Вы хватаете стакан, ожидая освежающего глотка, но вместо этого вы чувствуете удручающий вес пустого стакана. Это ваша личная ошибка 1009.
В ActionScript, если вы делаете это:
1
2
|
var s:String;
trace(s.toUpperCase());
|
При запуске кода Flash будет работать очень быстро (технический термин «выдает ошибку»). Переменная s
может быть объявлена, но ее значение равно null
(мы никогда не устанавливали значение, просто объявляли переменную), поэтому вызов метода toUpperCase
для нее проблематичен.
Для ясности, поскольку s
объявлен как String
, компилятор не имеет проблем с кодом: есть переменная с именем s
, это String
, а toUpperCase
является допустимым методом для вызова на String
s. Ошибка, которую мы получаем, является ошибкой во время выполнения , что означает, что мы получаем ее только при запуске SWF. Только когда логика выполнена, мы можем теперь увидеть, как это получается.
Шаг 2: Разрешить отладку
Как и в случае любой ошибки во время выполнения, иногда довольно легко сказать, что происходит, без какой-либо дополнительной информации. Но в других случаях полезно сузить это дальше. На этом этапе попробуйте включить «Разрешить отладку». Когда эта опция включена, вы получаете ошибки, которые также дают номера строк. Кроме того, вы можете «Отладить фильм», нажав Command-Shift-Return / Control-Shift-Enter.
Для этого см. Общую статью с советами по отладке «Исправление ошибок в AS3».
Иногда этого достаточно. Знание конкретной строки может быть всей необходимой вам информацией. Если нет, мы углубимся в следующий шаг.
Наш дорогой редактор, Майкл Джеймс Уильямс, изложил смысл этого шага в известной статье, которую я с радостью представляю вам сейчас с его любезного разрешения:
Ошибка AS3 один-о-о-девять
Никогда не очень хороший знак.
Не нужно беспокоиться,
Хит Ctrl-Shift-Return
И это точно определит причину (ну, линию).
Шаг 3: Начните трассировку
Если вы обнаружили нарушающую линию, но все еще не знаете, что происходит, выделите ее. Возьмите каждую переменную в этой строке и проследите их до ошибки.
Поскольку ошибка возникает при доступе к свойству или при вызове метода с нулевой переменной, для покрытия ваших баз вы должны отслеживать любые переменные и свойства, за которыми сразу следует точка. Например, возьмем эту строку кода:
1
|
myArray.push(someSprite.stage.align.toLowerCase());
|
Следует признать, что это довольно надуманный кусок кода, для которого я не представляю практического применения, но вы должны определить четыре возможных null
значения, к которым обращаются с помощью точки:
-
myArray
: мы вызываем методpush
для этой переменной -
someSprite
: мы получаем доступ к свойствуstage
-
stage
: мы получаем доступ к свойствуalign
-
align
: мы вызываем методtoLowerCase
Таким образом, ваш код отладки может выглядеть так:
1
2
3
4
|
trace(«myArray: «, myArray);
trace(«someSprite: «, someSprite);
trace(«someSprite.stage: «, someSprite.stage);
trace(«someSprite.stage.align: «, someSprite.stage.align);
|
Порядок важен; если someSprite
является нулевым объектом, но вы проверяете someSprite.stage.align
перед тестированием someSprite
, вы получите менее someSprite
результаты.
Теперь здравый смысл также играет в этом. В моем примере, если stage
существует, align
будет, безусловно, значение; Stage
всегда имеет настройку align
, даже если это значение по умолчанию.
Как правило, вы увидите что-то вроде следующего:
1
2
3
4
|
myArray: […stuff in the array…]
someSprite: [object Sprite]
someSprite.stage: null
Error #1009: …
|
Что должно указывать на то, что свойство stage
имеет значение null
, и теперь вы можете исправить его.
Шаг 4: Поиск решения
Самое простое решение — заключить ошибочный оператор в блок if и запускать блок только в том случае, если рассматриваемая переменная не равна нулю. Итак, предполагая, что в нашем предыдущем примере это была фактически null
stage
, мы могли бы сделать что-то вроде этого:
1
2
3
|
if (someSprite.stage) {
myArray.push(someSprite.stage.align.toLowerCase());
}
|
Этот тест — if (someSprite.stage)
— вернет true
если есть значение (независимо от значения), и false
если оно равно null
. В целом эта запись работает; вы всегда можете использовать if (someSprite.stage != null)
если хотите. Number
s представляет немного другую ситуацию, хотя. Если Number
имеет значение 0
, то технически оно имеет значение, но проверка if (someNumberThatEqualsZero)
оценивается как false
. Для Number
s вы можете использовать isNaN()
чтобы определить, хранится ли допустимое числовое значение в данной переменной.
В любом случае, эта техника — простой способ обойти ошибку. Если переменная, с которой мы хотим выполнить операцию, не установлена, не делайте эту операцию. Если в нашем стакане нет вкусного напитка, не поднимайте его. Достаточно просто.
Но такой подход возможен только в том случае, если логика является необязательной. Если логика требуется, то, возможно, вы можете указать значение по умолчанию для сомнительной переменной перед операцией ошибки. Например, если myArray
потенциально может быть null
, но обязательно, чтобы это было не так, мы можем сделать это:
1
2
3
4
|
if (!myArray) {
myArray = [];
}
myArray.push(someSprite.stage.align.toLowerCase());
|
Сначала будет проверено, является ли массив null
. Если это так, инициализируйте его пустым массивом (пустой массив является допустимым значением. Он может быть пустым, но это не массив, а массив) перед запуском надуманной строки кода. Если он не null
, перейдите прямо к надуманной строке кода. В реальных условиях, если наш стакан пуст, то наполните его вкусным напитком, прежде чем поднять его.
Кроме того, если myArray
является свойством экземпляра класса, в котором выполняется эта строка кода, вы можете довольно безопасно обеспечить допустимое значение, инициализируя свои свойства при инициализации объекта.
Что если логика требуется, но рассматриваемая переменная не так легко находится под нашим контролем? Например, что, если требуется наша искусственная строка кода, но сомнительной переменной является someSprite.stage
? Мы не можем просто установить свойство stage
; это внутренне контролируется DisplayObject
и DisplayObject
только для нас, простых смертных. Тогда вам может понадобиться стать хитрым и прочитать следующий шаг.
Шаг 5: Работа с null
стадией
Вероятно, существует бесконечное количество сценариев, в которых данная переменная или свойство могут быть null
. Очевидно, я не могу охватить их все в кратком совете. Однако есть одна конкретная ситуация, которая возникает снова и снова.
Допустим, вы пишете некоторый код, который выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
|
public class QuickSprite extends Sprite {
public function QuickSprite() {
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
}
private function onMove(e:MouseEvent):void {
var color:ColorTransform = new ColorTransform();
color.color = stage.mouseX / stage.stageWidth * 0xFFFFFF;
this.transform.colorTransform = color;
}
}
|
Еще один надуманный фрагмент кода (который может вызвать приступы — считайте, что вас предупредили), но в основном идея заключается в том, что у вас есть подкласс Sprite
и вы устанавливаете его как класс для клипа на сцене, используя Flash IDE.
Однако вы решаете, что хотите работать с этими QuickSprite
программно. Итак, вы попробуйте это:
1
2
|
var qs:QuickSprite = new QuickSprite();
addChild(qs);
|
И вы получите проклятую ошибку 1009. Почему? Потому что в конструкторе QuickSprite
вы QuickSprite
доступ к свойству stage
(унаследованному от DisplayObject
). Когда объект создается полностью с помощью кода, он не находится на стадии в тот момент, когда выполняется эта строка кода, что означает, что stage
имеет значение null
и вы получите ошибку. QuickSprite
добавляется в следующую строку, но это не достаточно скоро. Если экземпляр создается путем перетаскивания символа из библиотеки на сцену, то за кулисами работает немного магии, которая гарантирует, что экземпляр находится на сцене (то есть свойство stage
установлено) во время конструктор
Итак, вот что вы делаете: вы проверяете наличие значения для stage
. В зависимости от результата, вы можете сразу запустить настроенный код или настроить другой приемник событий, когда QuickSprite
будет добавлен на сцену. Что-то вроде этого:
01
02
03
04
05
06
07
08
09
10
11
|
public function QuickSprite() {
if (stage) {
init();
} else {
this.addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e:Event=null) {
this.removeEventListener(Event.ADDED_TO_STAGE, init);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
}
|
Если мы переместим линию stage
в другую функцию и вызовем эту функцию только тогда, когда у нас есть сцена, тогда мы настроены. Если stage
существует с самого начала, продолжайте и сразу же запустите init()
. Если нет, мы будем использовать init()
в качестве функции прослушивания событий для ADDED_TO_STAGE
, после чего у нас будет сценическое значение и мы сможем выполнить код. Теперь мы можем использовать класс для подключения к IDE Sprite или полностью программно.
Это хорошо работает и в классах документов. Когда вы сами запускаете SWF, класс документа имеет немедленный доступ к stage
. Если вы загрузите этот SWF-файл в другой SWF-файл, код в стеке инициализации класса документа будет выполнен до того, как загруженный SWF-файл будет добавлен на экран. Подобный трюк с проверкой stage
позволит вам работать с SWF как с автономной частью, так и с SWF, загруженным в содержащий SWF.
Это все
Спасибо за чтение этого Быстрого Подсказки! Я надеюсь, что вы немного осведомлены о том, как происходит ошибка 1009, и как вы можете ее отладить. Оставайтесь с нами, чтобы узнать больше подсказок о других распространенных ошибках.