Hidden features of Objective-C [closed]

假如想象 提交于 2019-11-27 16:33:32
benzado

Method Swizzling

Basically, at runtime you can swap out one implementation of a method with another.

Here is a an explanation with code.

One clever use case is for lazy loading of a shared resource: usually you would implement a sharedFoo method by acquiring a lock, creating the foo if needed, getting its address, releasing the lock, then returning the foo. This ensures that the foo is only created once, but every subsequent access wastes time with a lock that isn't needed any more.

With method swizzling, you can do the same as before, except once the foo has been created, use swizzling to swap out the initial implementation of sharedFoo with a second one that does no checks and simply returns the foo that we now know has been created!

Of course, method swizzling can get you into trouble, and there may be situations where the above example is a bad idea, but hey... that's why it's a hidden feature.

Posing

Objective-C permits a class to entirely replace another class within an application. The replacing class is said to "pose as" the target class. All messages sent to the target class are then instead received by the posing class. There are some restrictions on which classes can pose:

  • A class may only pose as one of its direct or indirect superclasses
  • The posing class must not define any new instance variables which are absent from the target class (though it may define or override methods).
  • No messages must have been sent to the target class prior to the posing.

Posing, similarly to categories, allows globally augmenting existing classes. Posing permits two features absent from categories:

  • A posing class can call overridden methods through super, thus incorporating the implementation of the target class.
  • A posing class can override methods defined in categories.

An example:

@interface CustomNSApplication : NSApplication
@end

@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu
{
     // do something with menu
}
@end

class_poseAs ([CustomNSApplication class], [NSApplication class]);

This intercepts every invocation of setMainMenu to NSApplication.

pfeilbr

Object Forwarding/Method Missing

When an object is sent a message for which it has no method, the runtime system gives it another chance to handle the call before giving up. If the object supports a -forward:: method, the runtime calls this method, passing it information about the unhandled call. The return value from the forwarded call is propagated back to the original caller of the method.

-(retval_t)forward:(SEL)sel :(arglist_t)args {
  if ([myDelegate respondsTo:sel])
 return [myDelegate performv:sel :args]
 else
 return [super forward:sel :args];
 }

Content from Objective-C Pocket Reference

This is very powerful and is used heavily in the Ruby community for the various DSLs and rails, etc. Originated in Smalltalk which influenced both Objective-C and Ruby.

ISA Switching

Need to override all of an object's behaviors? You can actually change the class of an active object with a single line of code:

obj->isa = [NewClass class];

This only changes the class that receives method calls for that object; it doesn't change the object's layout in memory. Thus, this is only really useful when you have a set of classes with the same ivars (or one with a subset of the others') and you want to switch between them.

One piece of code I've written uses this for lazy loading: it allocates an object of class A, fills a couple critical ivars (in this case, mainly a record number) and switches the isa pointer to point to LazyA. When any method other than a very small set like release and retain is called, LazyA loads all the data from disk, finishes filling in the ivars, switches the isa pointer back to A, and forwards the call to the real class.

#include <Foundation/Debug.h>

Lots of tools for trying to track down memory leaks, premature deallocs, and more in that header file.

Categories

Using Categories, you can add methods to built-in classes without subclassing. Full reference.

It's nice to add convenience methods to commonly used classes, such as NSString or NSData.

Objective-C Runtime Reference

It's easy to forget that the syntactic sugar of Objective-C is converted to normal C function calls that are the Object-C Runtime. It's likely that you will never need to actually delve into and use anything in the runtime. That is why I would consider this a 'hidden feature'.

Let me give a way one might use the runtime system.

Let's say that someone is designing an external framework API that will be used by third parties. And that someone designs a class in the framework that abstractly represents a packet of data, we'll call it MLAbstractDataPacket. Now it's up to the application who is linking in the framework to subclass MLAbstractDataPacket and define the subclass data packets. Every subclass must override the method +(BOOL)isMyKindOfDataPacket:(NSData *)data.

With that information in mind...

It would be nice if MLAbstractDataPacket provided a convenience method that returned the correct initialized class for a packet of data that comes in the form +(id)initWithDataPacket:(NSData *)data.

There's only one problem here. The superclass doesn't know about any of its subclasses. So here you could use the runtime method objc_getClassList() along with objc_getSuperclass() to find the classes that are subclasses of MLAbstractDataPacket. Once you have a list of subclasses you can then try +isMyKindOfDataPacket: on each until one is found or not found.

The reference information about this can be found at http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html.

I like the verbose method naming like [myArray writeToFile:myPath atomically:YES], where every argument has a label.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!