2014-12-25 5 views
3

Я работаю над классом, который содержит изображение и рисует его по центру 0, 0; для этого он извлекает высоту и ширину изображения и основывает свое смещение дисплея на этих значениях. Но, делая это ImageObserver в случае изображение еще не полностью загружено, я протечки this в конструкторе:Безопасно ли утечка этого ImageObserver в конструкторе?

public class Sprite extends SimplePaintable implements ImageObserver { 

    private final Image sprite; 
    private int xOffset; 
    private boolean xOffsetSet; 
    private int yOffset; 
    private boolean yOffsetSet; 

    public Sprite(Image sprite) { 
     this.sprite = sprite; 
     //warning: leaking this in constructor 
     int width = sprite.getWidth(this); 
     xOffset = width/2; 
     xOffsetSet = width != -1; 
     int height = sprite.getHeight(this); 
     yOffset = height/2; 
     yOffsetSet = height != -1; 
    } 

    @Override 
    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { 
     assert img == sprite; 
     if ((infoflags & WIDTH) != 0) { 
      xOffset = width/2; 
      xOffsetSet = true; 
     } 
     if ((infoflags & HEIGHT) != 0) { 
      yOffset = height/2; 
      yOffsetSet = true; 
     } 
     return !(xOffsetSet && yOffsetSet); 
    } 
    ... 

Сначала я думал, что это было хорошо, так как только смещенный переменными был неинициализированными и их значения по умолчанию были хорошими для (не) отображения незагруженного изображения, но затем я понял, что если бы имя, загруженное сразу как getWidth(this), было вызвано, теоретически можно было бы позвонить imageUpdate до завершения конструктора, в результате чего смещения будут правильно установлены в imageUpdate, а затем be unset от конструктора. Это проблема, или изображения загружаются синхронно только на EDT? Если это вызывает озабоченность, сделает метод imageUpdate synchronized, чтобы он не работал до завершения конструктора?

+0

Нет. Нельзя передавать 'this' из конструктора.Проблема (как вы догадались) заключается в том, что ваш экземпляр еще не был инициализирован. –

+0

Если 'Image' уже загружен,' observer' может быть 'null'. – trashgod

+0

@ ElliottFrisch В целом это не безопасно; однако, если 'Image.getWidth' гарантированно не вызывать' imageUpdate' до тех пор, пока конструктор не будет закончен, тогда это безопасно в этом конкретном случае. – Vitruvius

ответ

1

Утечка this в конструкторе является предупреждением, потому что оно могло привести к проблеме. Если переменные из super инициализируются, но this затем их модифицирует, но super сам протекает, тогда кто-то работает с еще не полностью инициализированными переменными.

Если вы уверены, что при построении никто не будет обращаться к каким-либо переменным, и нет другого способа сделать это (поскольку внешние библиотеки iE не позволяют вам делать это правильно), тогда это нормально игнорируйте это предупреждение.

В вашем случае теоретически возможно, что во время вызова sprite.getWidth(this) изображение вызывает наблюдателя для немедленного обновления хода, поэтому imageUpdate вызывается до завершения конструктора. В этом случае переменные смещения будут перезаписаны после инициализации конструктора. И нет, синхронизированный не предотвратит проблему, так как никто другой не удерживает блокировку в этот момент.

Есть несколько способов обойти это:

  • Используйте BufferedImage, который не требует наблюдателя для GetWidth/GetHeight.
    Даунсайд: изображение должно быть полностью загружено, что в некоторых случаях может привести к небольшой задержке (если i.E загружен через сеть).

  • Пользователь собственно замок:

    private final Object offsetLock = new Object(); 
    
    public Sprite(Image sprite) { 
        this.sprite = sprite; 
    
        synchronized(offsetLock) { 
         int width = sprite.getWidth(this); 
         xOffset = width/2; 
         xOffsetSet = width != -1; 
         int height = sprite.getHeight(this); 
         yOffset = height/2; 
         yOffsetSet = height != -1; 
        } 
    } 
    
    @Override 
    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { 
        assert img == sprite; 
        if ((infoflags & WIDTH) != 0) { 
         synchronized(offsetLock) { 
          xOffset = width/2; 
          xOffsetSet = true; 
         } 
        } 
        if ((infoflags & HEIGHT) != 0) { 
         synchronized(offsetLock) { 
          yOffset = height/2; 
          yOffsetSet = true; 
         } 
        } 
        return xOffsetSet && yOffsetSet; 
    } 
    

    Даунсайд: это будет вызывать синхронизируются, что может вызвать небольшую задержку. Обычно вы не заметите, если это называется только несколько раз, но в критическом по времени цикле это может быть заметно.

  • Используйте внешнюю функцию для обновления данных после того, как строительство закончено:

    public Sprite(Image sprite) { 
        this.sprite = sprite; 
    } 
    
    protected updateOffsets() { 
        updateWidth(sprite.getWidth(this)); 
        updateHeight(sprite.getHeight(this)); 
    } 
    
    protected updateWidth(final int newWidth) { 
        if (newWidth != -1) { 
         xOffset = newWidth/2; 
         xOffsetSet = true; 
        } 
    } 
    
    protected updateHeight(final int newHeight) { 
        if (newHeight!= -1) { 
         yOffset = newHeight/2; 
         yOffsetSet = true; 
        } 
    } 
    
    @Override 
    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { 
        assert img == sprite; 
        if ((infoflags & WIDTH) != 0) { 
         updateWidth(width); 
        } 
        if ((infoflags & HEIGHT) != 0) { 
         updateHeight(height); 
        } 
        return xOffsetSet && yOffsetSet; 
    } 
    

    Даунсайд: кто-то должен вызвать метод updateOffsets(), и до того, что объект не полностью инициализирован, что может привести к ошибкам или требует от вас написать метод строителя, что еще более усложняет ситуацию.

+0

Решил разбить ImageObserver на вспомогательный класс, чтобы скрыть метод imageUpdate, проверить, было ли полученное изображение BufferedImage, а если нет, для синхронизации на вспомогательном ImageObserver. – Vitruvius

+0

Кроме того, я понял, что возвращаемое значение 'imageUpdate' было противоположным правильному и отредактированному - вы хотите отредактировать свое соответствие, чтобы избежать путаницы. – Vitruvius

Смежные вопросы