Quản lý bộ nhớ trong lập trình blackberry

Như đã giới thiệu ở bài trước kiến trúc của blackberry:

Untitled-300x144.png

Vì vậy quản lý bộ nhớ trong lập trình blackberry tương ứng với quản lý bộ nhớ với ngôn ngữ C++, QML (interface) và của QT Framework. Bài viết này tôi sẽ giới thiệu với các bạn cách quản lý bộ nhớ của QTFramework với ngôn ngữ C++.

Meta-Object System

QT extends C++ với meta-object system nhằm đưa vào các tính năng mới như introspection mà không thể tìm thấy ở ngôn ngữ tĩnh (statically compiled language) như C++. Đằng sau đó, QT sử dụng meta-object compiler (MOC) để sinh ra các đoạn code cho việc khai báo Q_OBJECT macro và cho class signals (các bạn có thể tham khảo trên trang chủ của QTFramework). Cuối cùng, hàm QObject::connect gọi hàm MOC-generated introspection để kết nối signals và slots với nhau. Khi building ứng dụng Cascade MOC được gọi bởi build system.

Parent-Child Ownership

Cho đoạn code sau:

ApplicationUI::ApplicationUI(bb::cascades::Application *app) : QObject(app) {

Page *page = new Page();

Container *contentContainer = new Container();

contentContainer->setLayout(StackLayout::create());

TextField* textfield = TextField::create(); Textfield->setObjectName("textfield"); Slider* slider = Slider::create();

slider->setFromValue(0);

slider->setToValue(100);

contentContainer->add(textfield);

contentContainer->add(slider);

QObject::connect(slider, SIGNAL(immediateValueChanged(float)), this, SLOT(onImmediateValueChanged (float)));

page->setContent(contentContainer);

app->setScene(page);

}

void ApplicationUI::onImmediateValueChanged(float value) {

value = round(value);

QString stringValue = QString::number(value);

Application* app = static_cast<Application*>(this->parent());

TextField* textField = app->scene()->findChild<TextField*>("textfield");

textField->setText(stringValue);

} Nếu bạn xem xét kỹ. Bạn sẽ thấy rằng bạn không giải phóng object đã allocated với toán tử new. Điều này có thể dẫn đến leak nhưng không. Cascade widget được sắp xếp theo quan hệ parent-child để xử lý object ownership và memory management. Trong đoạn code trên thì root parent của toàn bộ object hierarchy là bbL::cascade::Application app object. Bộ nhớ của child controls sẽ được giải phóng khi object được delete lúc runtime.

QObject Memory Management

QObject được sắp xếp theo quan hệ parent-child. Bạn có luôn luôn set QObject’s parent trong suốt quá trình construction hoặc gọi tường minh bằng hàm QObject::setParent(QObject *parent). Parent lấy ownership của QObject và add nó vào list children. Bất cứ khi nào parent được deleted children cũng sẽ được deleted theo. Kĩ thuật này đặc biệt tốt với GUI objects, được dùng nhằm mục đích để xây dựng chúng thành các Objects trees. Sau đây là vài điều bạn nên ghi nhớ khi sử dụng kĩ thuật parent-child memory managment .

Nếu bạn xoá 1 QObject, destructor của nó tự động gỡ nó ra khỏi danh sách parent’list children. Signals và slots cũng bị disconnected để deleted object không thể nhận signals trước đó nó đã handle. Bạn nên không bao giờ mix các kĩ thuật quản lý bộ nhớ với nhau. Ví dụ bạn không nên quản lý 1 object mà sử dụng cả parent-child relationships và smartpointer(C++) (Cả 2 kĩ thuật này sử dụng reference counts riêng và sẽ conflict nếu sử dụng trên cùng 1 object). Bạn có thể, tuy nhiên sử dụng smart pointers và parent-child relationships trên cùng 1 ứng dụng trên các object riêng biệt. ( thậm chí bạn có thể sử dụng smart pointer như member variable của một QObject instance) Cho các đoạn code sau:

/*

  • Instrument.h
  • Created on: 09-12-2014
  •  Author: nguyenhaidang
    

*/

ifndef INSTRUMENT_H_

define INSTRUMENT_H_

include

namespace abc {

class Instrument: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString symbol READ symbol WRITE setSymbol NOTIFY symbolChanged)
    Q_PROPERTY(double price READ price NOTIFY priceChanged)

public:
    Instrument();
    virtual ~Instrument();

    QString symbol() const;
    void setSymbol(const QString& symbol);

    virtual double price() const = 0;
signals:
    void symbolChanged();
    void priceChanged();
private:
    QString m_symbol;
};

} /* namespace abc */

endif /* INSTRUMENT_H_ */

/*

  • Instrument.cpp
  • Created on: 09-12-2014
  •  Author: nguyenhaidang
    

*/

include "Instrument.h"

namespace abc {

Instrument::Instrument(QObject *parent): QObject(parent), m_symbol("")
{
    // TODO Auto-generated constructor stub

}

Instrument::~Instrument()
{
    // TODO Auto-generated destructor stub
}

void Instrument::setSymbol(const QString& symbol){
    if(m_symbol == symbol)return;
    m_symbol = symbol;
    emit symbolChanged();
}

QString Instrument::symbol()const{
    return m_symbol;
}

} /* namespace abc */

/*

  • Stock.h
  • Created on: 22-12-2014
  •  Author: nguyenhaidang
    

*/

ifndef STOCK_H_

define STOCK_H_

include "Instrument.h"

namespace abc {

class Stock: public Instrument {
    Q_OBJECT
    Q_PROPERTY(double spot READ spot WRITE setSpot NOTIFY spotChanged)
public:
    Stock(QObject* parent = 0);
    virtual ~Stock();
    double spot();
    void setSpot(double spot);
    double price() const;

signals:
    void spotChanged();
private:
    double m_spot;
};

} /* namespace abc */

endif /* STOCK_H_ */

include "Stock.h"

Stock::Stock(QObject* parent) : Instrument(parent), m_spot(0) { } Stock::~Stock() { // for illustration purposes only. Show that the destructor is called std::cout << "~Stock()" << std::endl; } double Stock::price() const{ return spot(); } double Stock::spot() const{ return m_spot; } void Stock::setSpot(double spot){ if(m_spot == spot) return; m_spot = spot; emit spotChanged(); } /*

  • Composite.h
  • Created on: 22-12-2014
  •  Author: nguyenhaidang
    

*/

ifndef COMPOSITE_H_

define COMPOSITE_H_

include "Instrument.h"

namespace abc {

class Composite: public Instrument
{
    Q_OBJECT
public:
    Composite(QObject* parent = 0);
    virtual ~Composite();

    void addInstrument(Instrument *instrument);
    bool removeInstrument(Instrument *instrument);
    const QList& instruments();
    double price() const;

signals:
    void instrumentAdded();
    void instrumentRemoved();

private:
    QList m_instruments;
};

} /* namespace abc */

endif /* COMPOSITE_H_ */

/*

  • Composite.cpp
  • Created on: 22-12-2014
  •  Author: nguyenhaidang
    

*/

include "Composite.h"

include

using namespace std;

namespace abc {

CompositeInstrument::CompositeInstrument(QObject* parent) : Instrument(parent) {
}
CompositeInstrument::~CompositeInstrument() {
    // for illustration purposes only to show that the destructor is called
    cout << "~CompositeInstrument()" << endl;     }          void CompositeInstrument::addInstrument(Instrument* instrument){         if(!m_instruments.contains(instrument)){             m_instruments.append(instrument);             instrument->setParent(this);
        emit instrumentAdded();
    } }

bool CompositeInstrument::removeInstrument(Instrument* instrument){
    if(m_instruments.contains(instrument)){
        m_instruments.removeOne(instrument);
        instrument->setParent(0);
        emit instrumentRemoved();
        return true;
    }
    return false;
}

const QList<Instrument*>& CompositeInstrument::instruments(){
    return m_instruments;
}

double CompositeInstrument::price() const {
    double totalPrice = 0;
    for(int i = 0; i < m_instruments.length(); i++){             totalPrice += m_instruments[i]->price();
    }
    return totolPrice;
}

} /* namespace abc */ Composite class sử dụng QList instance để track các instrument của nó . Khi một Instrument được add vào composite composite nhận quyền ownership của instrument sử dụng method instrument->setParent(this). Tương tự khi một instrument được removed khỏi composite, composite removes nó khỏi list childrens cảu nó sử dụng instrument->setParent(0).Trong thực tế bạn nên luôn luôn comment lại behavior này để rõ ràng khi sử dụng.

int main(){ Stock* stock = new Stock; stock->setSymbol("myStock"); stock->setSpot(50); Composite* composite = new Composite(); composite->addInstrument(stock); std::cout << "Composite price is: " << composite->price()<<std::endl; delete composite; // more code goes here } Output:

Composite price is: 51.4532 ~CompositeInstrument() Stock was deleted Như bạn đã thấy, Stock instance đã được xoá khi Composite instance bị xoá. Cuối cùng chú ý rằng parent-child relationships phân biệt với class hierarchy. Bạn có thể set 1 QObject's parent cho bất kì QObject không cần phải sharing direct inheritance relationship. Cám ơn vì đã theo dõi 😃!