Solidity Referans Türleri ve Veri Konumları

Solidity Referans Türleri ve Veri Konumları

Merhabalar. Bu yazımızda Solidity'deki referans türlerini (reference types) ve veri konumlandırmasını (data location) örnekler yaparak öğreneceğiz.

Bilmemiz Gereken Bazı Kavramlar

Solidity dilindeki referans türlerini öğrenmeden önce bazı kavramların ne olduklarına göz atmamız gerekiyor. Solidity zincirinde bulunan yazıları linklere tıklayarak öğrenebilir ve tekrardan bu yazımıza dönebilirsiniz.

Solidity Referans Türleri

Referans türlerinin veriyi direkt içeren değişkenlerin adreslerini depoladığını öğrenmiştik. (ayr. bkz) Solidity yazılım dilinde referans türleri şunlardır:

  • Diziler (Arrays)
  • Sınıflar (Structs)
  • Adresleyiciler (Mappings)

Referans veri türleri depolanacaklarına alana dair net bir konuma ihtiyaç duyarlar. Veri konumu (data location) olarak ele alacağımız konuda memory, storageve calldata kavramlarını inceleyeceğiz ve örnekler içinde kullanacağız.

Veri Konumu

Veri konumu (data location), referans türlerinin konumlarının ne olduğu hakkında ek bilgi verir. Veri konumlandırıcılar ise şunlardır:

  • memory ➝ veriyi bellekte tutar
  • storage ➝ veriyi depoda tutar
  • calldata ➝ belleğe benzer

Referans türleri kullanıldığında yanında veri konum etiketlerinin- memory, storage, calldata- belirtilmesi gerekir.

Atama Davranışları

  1. memory ve storage arasındaki atamalarda her seferinde bağımsız kopya oluşturulur. Yani bir değişkende yapılan değişiklikler diğer değişkeni etkilemez.
  2. memory ve memory arasındaki atamalarda referans oluşturulur. Yani ana değişken, aynı veriyi referans alan değişkenlerden etkilenir. Yapılan değişiklikler ana değişkenle birlikte veriyi referans alan tüm değişkenlerin sonucunu değiştirir.
  3. storage 'dan yerel depoya (local storage) yapılan atamalarda referans oluşturulur. Referansa yapılan değişiklikler storage değişkeni etkiler.
  4. storage'a yapılan diğer tüm atamalarda bağımsız bir kopya oluşturulur.

Burada yazdıklarımın oldukça karışık olduğuna eminim. Bu yüzden takip eden satırlarda yazdıklarımı örneklerle daha açıklayıcı kılmaya çalışacağım. Ayrıca bu yazıyı tamamlamak için birden fazla yazıyı tamamlamam ve çeşitli araştırmalar yapmam gerekti. Çok önemli ve diğerlerine göre karışık bir konu olduğundan sık sık örnek yapmayı unutmayın.

// ----- Solidity Dökümanlarından Bir Örnek ------ //


// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;


contract C {
    // x değişkeni depoda (storage) tutulur. Ancak veri konumunun ihmal edildiği tek yer burasıdır.
    // Yani data location etiketi koyarsanız hata alırsınız. 
    uint[] x;
    // Örneğin storage etiketi koyulan aşağıdaki kod hatalıdır.
    //uint[] storage x;


    // memoryArray bir referans türü olduğu için veri konumu etiketiyle kullanılmalıdır. 
    // memoryArray fonksiyon parametresi olduğu için bellekte tutulur. calldata ya da memory etiketi alır.
    function f(uint[] memory memoryArray) public {
        x = memoryArray; // kod çalışır, bellekteki memoryArray'i depodaki x'e eşitlediğimiz için yeni bir kopya oluşturulur. (1. maddeye bakın!)
        // kod çalışır, yerel bir depo (local storage) olarak tanımlanan y, x'den referans aldığı için işaretleyici (pointer) görevi görür.
        // Yani y'de yapılan silme, ekleme vb. işlemler x'i doğrudan etkiler.
        uint[] storage y = x; 
        y[7]; // çalışır, y işaretleyicisi aracılığıyla x'in 8'inci elemanını alır.
        y.pop(); // çalışır, y işaretleyicisi aracılığıyla x'in son elemanını siler. Ayrıntı için Stack ve Heap yazısına bakın!
        delete x; // x'deki elemanlar silinir. Bu yüzden y işaretleyiciside silinir.
        // aşağıdaki kod çalışmaz! çünkü işaretleyici bir değere dönüştürülemez.
        // y = memoryArray;
        // İşaretçiler (pointer) veri konumlarını, adreslerini işaret ader. Bu yüzden net konumları yoktur. Silinmesi gibi bir işlem de bu yüzden yapılamaz.
        // delete y;
        // g fonksiyonuna x parametresini göndererek çağırıyoruz,
        // 3. maddede açıkladığımız duruma göre x'in bir referansı oluşturulur.
        // storage'dan storage'a atama olduğundan g fonksiyonunda x'e yapılan işlemler x'i doğrudan etkiler.
        g(x); 
        // h fonksiyonuna x parametresini göndererek çağırıyoruz,
        // storage'dan memory'e bir atama olduğundan bellekte bağımsız bir kopya oluşturulur. x fonksiyondaki işlemlerden etkilenmez. (4. maddede açıkladık.)
        h(x); 
    }


    function g(uint[] storage) internal pure {}
    function h(uint[] memory) public pure {
Referans türündeki fonksiyon parametreleri memory ya da calldata etiketi almak zorundadır.

Uzun zamandır üzerinde çalıştığım bu yazımı bitirmiş olmaktan olabildiğince mutluyum. Yorum satırlarıyla örnekleri anlaşılır kılmaya çalıştım. Konu hakkında sorularınız olursa bana ulaşabilirsiniz. Kendinize çok iyi bakın. Öğrenmeyi bırakmayın!

Yorumunu Bırak

Çok hızlısın. Biraz dinlendikten sonra tekrar devam edebilirsin.
Bugünlük gönderebileceğin kadar yorum gönderdin. Lütfen yarın tekrar dene.
Mesajınız bize başarılı bir şekilde ulaştırıldı. Teşekkürler.

Yorumlar

0 Yorum yok

Henüz yorum yapılmamış. İlk yorum yapan sen ol.

Blog Yazarı

Ömer Faruk Coşkun
Yazar
@ofcskn

İstanbul Üniversitesi Bilgisayar Mühendisliği Öğrencisi