Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObjC: memory usage of delegate vs block?

I'm comparing the memory footprint of delegate vs block in Objective-C, for solving a same problem. For example, there is a worker class who does some work:

// delegate
@protocol WorkerDelegate : NSObject
- (void)workHasBeenDone;
@end

// block
typedef void (^WorkerBlock)();

@interface Worker : NSObject

@property (nonatomic, weak) id<WorkerDelegate> delegate;

@property (nonatomic, copy) WorkerBlock block;

- (void)doTheWork;

@end

The code is self-explanatory, in order to know when a work has been done, I can use either the delegate or block:

@implementation MyObject

- (void)workHasBeenDone
{
    [self doCleanUp];
}

- (void)entryMethod
{
    Worker *worker = [Worker new];
    worker.delegate = self;
    // or:
    worker.block = ^{[self doCleanUp];};
    [worker doTheWork];
}

@end

As far as I know, in the above code, self as a delegate, is in memory; And the block is copied on heap, but I'm not sure, which has better memory footprint?

Now I need a number of workers:

Worker *workerA = ... // created and set delegate OR block for completion notification
Worker *workerB = ... // created and set delegate OR block for completion notification
Worker *workerC = ... // created and set delegate OR block for completion notification
...

NSDictionary *workers = @{
    "jobA": workerA,
    "jobB": workerB,
    ...
};

In this case, block seems to be cleaner, but still, does it have better, same or worse memory footprint?

Thanks a lot!

like image 928
hzxu Avatar asked Nov 06 '14 23:11

hzxu


1 Answers

A block is an ObjC object, so it comes with the same memory usage. You're on the right path thinking about multiple Workers — what happens if you want to do the same cleanup in response to all of them?

With a delegate:

workerA.delegate = workerB.delegate = workerC.delegate = self;

//...

- (void)workHasBeenDoneWithWorker:(Worker *)worker {
    //...
}

Here you get three weak references to the same object, so no additional storage or ownership requirements. And the same workHasBeenDoneWithWorker: method gets called three times. (Notice I changed it a little bit — it's good for a delegate method to know who called it, for exactly this reason: one object can be the delegate for multiple others, and it may want to know whose work it has been delegated.)

Now, with blocks:

workerA.block = workerB.block = workerC.block = ^{ [self doCleanUp]; };

Because your block is declared @property (copy), this gets you three copies of the block. Even though its source code is the same, the internal and captured state for each will be different. Also, there's no way (as declared) for the block to know whose work it's doing... and if you added a parameter to the block that references the Worker it belongs to, you'll have to be careful about reference cycles. The memory usage difference is trivial, but the API architecture difference is more significant.

In general, delegates work well when:

  • multiple delegating objects might share the same delegate
  • the same delegate method might be called multiple times for the same delegating object

And (completion-handler style) blocks work well when:

  • work will be done once, the block will be called once, and then the block will be discarded
  • completion tasks are so closely tied to setup tasks that the ability of a block to capture surrounding state is valuable
like image 75
rickster Avatar answered Sep 20 '22 00:09

rickster