Before you start reading this tutorial, please read about method swizzling.
The Objective-C model of object-oriented programming is based on message passing to object instances. In Objective-C one does not call a method; one sends a message. In Objective-C, the target of a message is resolved at runtime, with the receiving object itself interpreting the message. A method is identified by a selector or SEL — a unique identifier for each message name, often just a NUL-terminated string representing its name — and resolved to a C method pointer implementing it: an IMP.[18] A consequence of this is that the message-passing system has no type checking. The object to which the message is directed — the receiver — is not guaranteed to respond to a message, and if it does not, it raises an exception.[19]
In C++ this is how it will be called.
obj->method(argument);
This is how it gets called in objective-c.
[obj method:argument];
When Objective-C code is compiled, message sends are transformed into calls to a function called objc_msgSend
(literally “send a message to some object with an argument”).
@interface TestClass : NSObject
@property(nonatomic) BOOL dynamicProperty;
@property(nonatomic, direct) BOOL directProperty;
- (void)objectMethod;
- (void)directMethod __attribute__((objc_direct));
@end
// example is shown here
objc_msgSend(tc, @selector(dynamicMethod), NULL);
objc_msgSend
is responsible for determining which underlying implementation to call in response to this message, a process known as method dispatch.
If you are wondering, how Objective-C implements the responder chain, here is a quick explanation.
In Objective-C, each class (Class
) maintains a dispatch table to resolve messages sent at runtime. Each entry in the dispatch table is a method (Method
) that keys a selector (SEL
) to a corresponding implementation (IMP
), which is a pointer to a C function. When an object receives a message, it consults the dispatch table of its class. If it can find an implementation for the selector, the associated function is called. Otherwise, the object consults the dispatch table of its superclass. This continues up the inheritance chain until a match is found or the root class (NSObject
) deems the selector to be unrecognized.
Now, let’s get to the actual code, which implements the new attribute objc_direct. By adding this new attribute to the method name, this method will not be called via objc_msgSend. The advantage of this is that it saves time since its called directly.
@interface TestClass : NSObject
@property(nonatomic) BOOL dynamicProperty;
@property(nonatomic, direct) BOOL directProperty;
- (void)objectMethod;
- (void)directMethod __attribute__((objc_direct));
@end
@implementation TestClass
-(void)objectMethod {
NSLog(@"object method");
}
-(void)directMethod {
NSLog(@"direct method");
}
@end
TestClass* tc = [[TestClass alloc] init];
[tc objectMethod];
[tc directMethod];
//objc_msgSend(tc, @selector(objectMethod), NULL);
A direct method has the look and feel of a conventional method, but has the behavior of a C function. This is how you can implement a function in C.
static void callMethodDirectly(TestClass *__unsafe_unretained object) {
[object directMethod];
}
callMethodDirectly(tc);
By adding the attribute objc_direct_members to the class, all its members will made as direct.
__attribute__((objc_direct_members))
@interface TestClass : NSObject
@property(nonatomic) BOOL dynamicProperty;
@property(nonatomic, direct) BOOL directProperty;
- (void)objectMethod;
- (void)directMethod __attribute__((objc_direct));
@end
Helpful links:
https://reviews.llvm.org/rG11d47b3d553c6c3888745e96f460855aae541c48
https://nshipster.com/direct/
https://pspdfkit.com/blog/2020/improving-performance-via-objc-direct/
Leave a Reply