ব্লক ও এর ব্যবহার

ভূমিকাঃ সহজভাবে বলতে গেলে ব্লক হচ্ছে অবজেক্টিভ-সি এর anonymous function. Google ডেফিনেশন অনুযায়ী অ্যানোনিমাস ফাংশন হচ্ছে- An anonymous function is a function that is not stored in a program file, but is associated with a variable whose data type is function_handle. Anonymous functions can accept inputs and return outputs, just as standard functions do. However, they can contain only a single executable statement. এর মাধ্যমে আপনি বিভিন্ন অবজেক্টের মধ্যে ইচ্ছামত স্টেটমেন্ট (Satement) পাস তথা আদান-প্রদান করতে পারবেন যেমনভাবে সাধারণ ডাটা আদান প্রদান করে থাকেন। উপরন্তু ব্লককে ক্লোজার (Closure) হিসেবে ব্যবহার করা হয় যাতে করে তার পক্ষে তার আসে পাশের ডাটা/অবস্থা নিয়ে কাজ করাও সম্ভব হয়। আস্তে আস্তে আমরা এ ব্যপারে বিস্তারিত জানতে চেষ্টা করব।

সিনট্যাক্সঃ ডিক্লেয়ারেশন-

returntype (^blockName) (argumentType);

ইমপ্লিমেন্টেশন-

returntype (^blockName) (argumentType) = ^{
    // Statements
};

এখানে "=" চিহ্নের বাম পাশের অংশটি হচ্ছে একটি ব্লক ভেরিয়েবল এবং ডান পাশের অংশ হচ্ছে ব্লকটি।

উদাহরণ-

void (^simpleBlock) (void) = ^{
    NSLog(@"This is a block that does not return anything and also has no argument!");
};

উপরের এই ব্লককে আমরা simpleBlock(); এভাবে কল করতে পারি।

ব্লক (Block) তৈরিঃ ব্লক তৈরির ব্যাপারটা মোটা মুটি ফাংশন তৈরির মতই। যেভাবে আপনি একটি ফাংশন ডিক্লেয়ার করে থাকেন ঠিক সেভাবেই একটি ব্লক ভেরিয়েবল ডিক্লেয়ার করতে পারেন আবার যেভাবে একটি ফাংশন এর ইমপ্লিমেন্ট করেন সেভাবে একটি ব্লককে ডিফাইন করতে পারেন। এমনকি যেভাবে একটি ফাংশনকে কল করেন ঠিক সেভাবেই একটি ব্লককেও কল করতে পারেন। নিচে একটি সহজ উদাহরণ দেখানো হল,

// main.m

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        // Declare and define the isPlaceOpen block
        BOOL (^isPlaceOpen)(void) = ^ {
            return YES;
        };

        // Use the block
        NSLog(@"Open state of the place is: %hhd ", isPlaceOpen());
    }
    return 0;
}

"^" চিহ্নটি ব্যবহার করে isPlaceOpen কে একটি ব্লক হিসেবে চিহ্নিত করা হয়। এটাকে অবজেক্টিভ-সি এর "*" পয়েন্টার চিহ্নের মতই মনে করতে পারেন অর্থাৎ শুধুমাত্র ডিক্লেয়ার করার সময় এটা গুরুত্বপূর্ণ কিন্তু এর পরে এটাকে সাধারণ ভেরিয়েবলের মতই মনে করে ব্যবহার করতে পারেন। নিচে আরেকটি উদাহরণ দেখি যেখানে আমাদের ব্লকটি দুইটি double টাইপের প্যারামিটারও গ্রহণ করে এবং সেগুলো ব্যবহার করে অল্প কিছু হিসাব করার পর একটি double টাইপ ডাটা রিটার্ন করে।

// main.m

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        // Declare the block variable
        double (^priceAfterTax)(double rawPrice, double totalTax);

        // Create and assign the block to the variable
        priceAfterTax = ^(double price, double tax) {
            return price + tax;
        };

        // Call the block
        double total = priceAfterTax(400, 40);

        NSLog(@"Total price after considering tax is: " @"%.2f Tk.", total);
    }
    return 0;
}

ক্লোজার (Closure)ঃ শুরুতেই বলা হয়েছে যে, সাধারণভাবে একটি ব্লক, ক্লোজার হিসেবেও ইমপ্লিমেন্টেড হয়। যেমন নিচের উদাহরণটি দেখে আমরা বিশ্লেষণ করতে পারি এখানে ক্লোজার ফিচার কোথায়,

// main.m

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        NSString *foodGenre = @"Italian";

        NSString *(^getFullItemName)(NSString *) = ^(NSString *itemName) {
            return [foodGenre stringByAppendingFormat:@" %@", itemName];
        };

        NSLog(@"%@", getFullItemName(@"Pizza"));    // Honda Accord
    }
    return 0;
}

আবারও বলি, ফাংশনের মতই একটি ব্লকের মধ্যে থেকে আপনি সেটার লোকাল ভেরিয়েবলগুলো, তার কাছে যে প্যারামিটারগুলো পাস করা হয়েছে সেগুলো এবং যেকোনো গ্লোবাল ভেরিয়েবল অ্যাক্সেস করতে পারবেন। কিন্তু ক্লোজার হিসেবে ব্লকের ব্যবহারের ফলে আপনি কিছু নন-লোকাল ভেরিয়েবলকেও ব্লকের মধ্যে থেকে অ্যাক্সেস করতে পারবেন। নন-লোকাল ভেরিয়েবল হচ্ছে ব্লকের বাইরের কোন ভেরিয়েবল কিন্তু ব্লকের Lexical Scope এর আওতাভুক্ত। উপরের উদাহরণে foodGenre সেরকম একটি নন-লোকাল ভেরিয়েবল যেটাকে getFullItemName ব্লকের মধ্যে থেকেও ব্যবহার করা গেছে।

মেথড প্যারামিটার হিসেবে ব্লক ব্যবহারঃ একটি পূর্ণ ব্লককে যেকোনো মেথডের প্যারামিটার হিসেবেও ব্যবহার করা যায়। ওই ব্লক যে কাজটা করে মূলত সেই কাজটিকেই একটা প্যাকেজ সরূপ একটি প্যারামিটার হিসেবে যেকোনো মেথডের কাছে দেয়া যায়। নিচে সেরকম একটা উদাহরণ দেখবো আমরা। প্রথমে আপনার প্রজেক্টকে এমনভাবে সাজিয়ে নিন যাতে সেখানে Food.h, Food.m এবং main.m এই ৩টি ফাইল থাকে নিচের মত করে,

// Food.h

#import <Foundation/Foundation.h>

@interface Food : NSObject

- (void)performActionWithCompletion: (void (^) ()) completionBlock;

- (void)calculatePriceWithTax: (double (^) (double price)) taxCalculatorBlock;

@end
// Food.m

#import "Food.h"

@implementation Food

- (void)performActionWithCompletion:(void (^) ()) completionBlock {

    NSLog(@"Started cooking...");
    completionBlock();
}

- (void)calculatePriceWithTax: (double (^) (double price)) taxCalculatorBlock {
    NSLog(@"Total price is: %f", taxCalculatorBlock(400));
}

@end
// main.m

#import <Foundation/Foundation.h>
#import "Food.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Food *process = [[Food alloc]init];

        [process performActionWithCompletion: ^{
            NSLog(@"Cooking is finished and this block has been called to intimate action is performed.");
        }];

        [process calculatePriceWithTax: ^(double totalPrice) {
            return totalPrice + 40;
        }];

    }
    return 0;
}

ব্লক টাইপ ডিফাইন করাঃ typedef এর মাধ্যমে একটি ব্লক টাইপ তৈরি করা যায় যাতে করে ব্লক ব্যবহারের সময় এর সিনট্যাক্স এলোমেলো না হয়ে যায় এবং ওই ব্লকটিকে আরও সহজ এবং সংক্ষেপ নামে ব্যবহার করা যায়। এ ব্যাপারে এবং উপরের টপিক গুলোর আরও বিস্তারিত থাকবে আমাদের সম্ভাব্য কাগুজে বইয়ে। বই এর আপডেট পেতে লাইক দিয়ে রাখতে পারেন এখানে

Originally Posted Here

Last updated