রিটেইন কাউন্ট – retainCount কী?
আধ্যাত্বিক উত্তর হলো – এইটা মেমরি ম্যানেজমেন্টের প্রাণ।
আর টেকনিক্যাল উত্তর হলো –
NSObject নামে একটা ক্লাস আছে। জাভাতে যেমন Object ক্লাস – অনেকটা ওইরকম। অবজেক্টিভ-সি বা সুইফট এর দুনিয়াতে আমরা যত অবজেক্ট তৈরি করি তাদের (প্রায়) সবার সুপার ক্লাস হলো NSObject। আর retainCount হলো NSObject এর একটা unsigned integer (NSUInterger) প্রপার্টি।
তার মানে একটা অব্জেক্টিভ-সি বা সুইফট প্রোগ্রামে যত অবজেক্ট তৈরি হয়, তাদের (প্রায়) সবার এই retainCount প্রপার্টি আছে।
(বারবার কেন “প্রায়” বলছি সেটা এখানে আলোচনা করা হবে না। এখানে আমি যতবার “অবজেক্ট” কথাটি ব্যবহার করব, ধরে নিতে হবে তারা সবাই NSObject এর সাবক্লাস)
এখন, সব অবজেক্ট এরই যেহেতু retainCount প্রপার্টি থাকে, তাই retainCount আসলে খুবই গুরুত্বপূর্ন একটা প্রপার্টি। এর সম্পর্কে জানাও তাই খুব জরুরী। তাহলে দেখা যাক – এর কাজ কী!
- retainCount একটা unsigned পুর্ণসংখ্যা। তাই এখানে শুধু ০ বা ০ থেকে বড় কিছু রাখা যায়। নেগেটিভ কিছু রাখা যায় না
- নাম থেকেই বোঝা যাচ্ছে এটা কিছু একটা গননা (count) করে।
- সচরাচর আমরা প্রোগ্রামাররা একে ব্যবহার করি না। এর কাজ চলে অনেকটা গোপনে।
- কোন একটা অবজেক্টকে অন্য যত অবজেক্ট যত যায়গা থেকে মালিকানা দাবি করে, retainCount সেই সংখ্যা গননা করে রাখে।
অর্থাৎ আমি আমার কোন ক্লাসে দাঁড়িয়ে যদি একটা অবজেক্ট তৈরি করি এভাবে –
ClassName *object = [[ClassName alloc] init]; // অবজেক্টিভ-সি
var object = ClassName() // সুইফট
তাহলে আমি যে ক্লাসে দাঁড়িয়ে ওই অবজেক্ট তৈরি করলাম, সে হলো ওই অবজেক্ট এর একজন মালিক। এর ফলে object টির retainCount ১ হবে।
একইসাথে একই অবজেক্টের মালিকানা অনেকে নিতে পারে। আবার একজন একাই একাধিকবার মালিকানা দাবি করতে পারে। এভাবে যতজন যতবার ওই অবজেক্ট এর মালিক হয়েছে সেটা একটা গণনাযোগ্য বিষয়। মালিকানা যেমন নেয়া যায়, তেমনি কাজ শেষ হয়ে গেলে মালিকানা প্রত্যাহার করাও যায়। কোন অবজেক্ট এর যখন কোন মালিক না থাকে (orphan), তখন আর তা প্রত্যাহার করারও কোন ব্যাপার থাকে না।
একটি বিশেষ মুহুর্তে কোন একটি অবজেক্ট এর ০ বা ততোধিক মালিক থাকে। এই যে বিশেষ মুহুর্তে একটা অবজেক্ট এর কতজন মালিক সেই সংখ্যাটা গুনে রাখা হয়। কে কে মালিক তা না জানলেও, কতজন মালিক তা জানা থাকে। একটা অবজেক্ট এর retainCount তার এই মুহুর্তের মালিকের সংখ্যা কত, সেই মান জমা রাখে।
- retainCount একটা রিড-অনলি প্রপার্টি। বাইরে থেকে একে সেট করা যায় না। এর মান বাড়ানো কমানোর নির্দিষ্ট উপায় আছে।
- retainCount এর মান ০ হয়ে যাওয়া মানে হলো – গারবেজ কালেক্টর এখন এই অবজেক্ট কে ডি-অ্যালোকেট করতে পারবে, এবং মুহুর্তের মধ্যেই তা করা হবে।
- আমার যদি কোন অবজেক্ট কে বাঁচিয়ে রাখার দরকার পড়ে, তাহলে খেয়াল রাখতে হবে যেন এর retainCount ০ এর থেকে বেশি থাকে।
আবার কোন এক সময়, আমি যদি একটা অবজেক্ট এর মালিকানা দাবি করি, এবং আমার ওকে ব্যবহার করা শেষ হলে মালিকানা প্রত্যাহার না করেই ওর রেফারেন্সটা হারিয়ে ফেলি, তাহলে আমি যে মালিকানা দাবি করেছিলাম সেটা আর কেউ প্রত্যাহার করতে পারবে না। সুতরাং ওই অবজেক্ট আর ডি-অ্যালোকেট হবে না। এরকম হওয়াকেই বলে মেমরি লিক। একটা সহজ উদাহরণ দিই-
ClassName *object = [[ClassName alloc] init];
ধরুন এভাবে মালিকানা নিলাম। ফলে object এর retainCount হলো ১। এরপর আমার কাজ শেষ হলে আমাকেই মালিকানা প্রত্যাহার করে নিতে হবে। মালিকানা প্রত্যাহার না করেই যদি-
object = অন্য কোন অবজেক্ট
সেট করে ফেললাম। এর ফলে object ভেরিয়েবল এ আগে যে অবজেক্টটা ছিল তার রেফারেন্স কিন্তু হারিয়ে গেল, তবু মেমরির কোথাও না কোথাও ঐ অবজেক্টের দখল করা অংশটা ওর দখলেই থেকে গেল। গারবেজ কালেক্টর ঐ অংশটাকে কখনোই ডি-অ্যালোকেট করতে পারবে না, কারণ ওখানে যে অবজেক্ট আছে তার retainCount > ০ । এভাবে কিছুটা মেমরি লিক হয়ে গেলো।
সুতরাং মালিকানা প্রত্যাহার করার জন্যে আমাকে আগে-
[object release];
কল করতে হবে। ফলে object এর retainCount ০ হয়ে যাবে। তার আর কোন রেফারেন্স কারো কাছে না থাকলে গারবেজ কালেক্টর তাকে free করে দিতে পারবে।
এখন এই object ভেরিয়েবলে অন্য কোন অবজেক্ট সেট করা যাবে। তাহলে আর মেমরি লিক হবে না।
(যারা ARC আসার পর থেকে কাজ শুরু করেছে, তাদের এই release সম্পর্কে ধারনা থাকার কথা না, কারণ ARC নিজেই জায়গামত এগুলো বসিয়ে দেয়, তবু এই ব্যাপারটা জানা থাকা দরকার, তাই লিখছি)
কোন অবজেক্ট এর মালিকানা ৩ ভাবে নেয়া যায় –
- alloc init
- retain
- copy
আর শুধু ১ ভাবে মালিকানা ছেড়ে দেয়া যায়। – release
ছবিতে ক্লিক করে বড় করে দেখুন। এখানে ৩ টা অবজেক্টের জীবনচক্র দেখানো হয়েছে।
একবার করে alloc init অথবা retain অথবা copy করলে ওই অবজেক্ট এর retainCount এক করে বাড়ে। আর একবার করে release করলে retainCount এক করে কমে।
যার retainCount ০, তাকে release করতে গেলে একটা রানটাইম এরর দিবে। আর যার retainCount ০ এর বেশি, কোনভাবে তার রেফারেন্স হারিয়ে ফেললে মেমরি লিক হবে। আর সে কারণেই retainCount খুব গুরুত্বপূর্ন এবং মজার।
কোন অবজেক্ট এর মালিকানা যে নেয়, তার নিজের দায়িত্ব এটাকে ছেড়ে দেয়া। অন্য কেউ এটা ছাড়বে এই আশা করলে ভুল হবে। এমনিতে এই ব্যাপারগুলো বেশ জটিল, কিন্তু অবজেক্টিভ-সি এই কঠিন বিষয়টাকেই অনেক সহজ করে দিয়েছে।
আগামীতে এই ব্যাপারটি নিয়ে কেস-বাই-কেস আলোচনা করা হবে। শেষ পর্যন্ত আমি দেখাতে চেষ্টা করব – কেন ARC এর যুগেও এই বিষয়টা সম্পর্কে পরিষ্কার ধারনা থাকা জরুরি। বিশেষ করে সুইফট, যেখানে ACR বাধ্যতামূলক, সেখানেও।
যারা এই সিরিজটা পড়বেন, তারা যদি অব্জেক্টিভ-সি তে ARC-মুক্ত একটা ছোট প্রোজেক্ট তৈরি করে এই ব্যাপারগুলি ঝালিয়ে নেন, তাহলে খুব ভাল করবেন। মুল উদ্দেশ্য মেমরি ম্যানেজমেন্ট বোঝানো হলেও, এর পাশাপাশি আরও কিছু মৌলিক কিন্তু প্রাসঙ্গিক বিষয় আলোচনা চলে আসবে।