Blocks instead of performSelectorwithObjectafterDelay
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In modern Objective-C, blocks are usually a better choice than performSelector:withObject:afterDelay: for delayed execution. The usual replacement is dispatch_after, because it is clearer, more flexible, and better suited to passing context than the older selector-based API.
Why the Selector-Based API Feels Limited
The classic delayed-selector pattern still works:
But the design has several drawbacks:
- the delayed action is split across two methods
- only one object argument is passed directly
- the selector itself is less expressive than inline code
- scheduling behavior is tied to older run-loop style thinking
For small delays this may be fine, but once the action needs more context, queue choice, or cancellation logic, blocks become easier to maintain.
Use dispatch_after for the Modern Block-Based Pattern
The common replacement is Grand Central Dispatch:
This expresses the delay, target queue, and delayed action in one place. That makes the flow easier to read because you do not have to jump between a selector call and a separate callback method just to understand what will happen later.
For UI work, the main queue is the correct choice. For background work, choose a background queue explicitly.
Blocks Make Passing Context Much Easier
One of the biggest practical benefits of blocks is that they capture local variables naturally.
With performSelector, you would often need an extra method just to carry one delayed value. With a block, the surrounding state is already available. That becomes even more valuable when the delayed logic needs several local values.
Queue control is also more explicit:
That is harder to express cleanly with the old selector API.
Design Cancellation and Ownership Explicitly
One advantage of the old delayed-selector approach was the paired cancellation API. With blocks, cancellation is not automatic just because you used dispatch_after. If cancellation matters, represent the work item directly:
Also remember that blocks capture objects strongly by default. If the delayed block may outlive the current interaction, a weak reference to self can prevent retain cycles:
This is not unique to dispatch_after, but block capture rules become part of the design once you switch away from selectors.
Common Pitfalls
The biggest mistake is replacing performSelector with blocks mechanically while ignoring queue choice. UI updates still belong on the main queue, and background work should not touch UIKit directly.
Another issue is assuming dispatch_after is automatically cancellable. If cancellation matters, create and manage a cancellable block or choose a timer abstraction that matches the problem better.
Developers also sometimes overlook retain cycles when a delayed block captures self strongly and is also retained by that same object.
Finally, if the real problem is debouncing repeated events or scheduling recurring work, a timer or a dedicated scheduling abstraction may fit better than stacked delayed blocks.
Summary
- Blocks are usually a cleaner replacement for
performSelector:withObject:afterDelay:. - '
dispatch_afteris the standard modern Objective-C tool for delayed execution.' - Blocks are easier to read because they keep the delayed action close to the scheduling call.
- They also capture local context naturally, which makes delayed code more flexible.
- If you need cancellation or careful ownership, design that explicitly when moving to a block-based approach.

