Introspection: It is the ability to provide information about objects/classes at runtime . Objective C runtime supports introspection. A small sample of the type of information provided by the objC runtime
1) names of methods of class from a class object.
2) information about method arguments
3) implementation(IMP) of individual methods of a class
4) Information about instance variables of a class
This list is pretty long. Look at the "Objective C runtime reference" section of the Apple documentation for a comprehensive list of information which the objC runtime can provide.
Here is some sample code to test the introspection abilities of the objC runtime. The following code displays the methods supported by a particular class and its super classes including private methods.
A call like classListMethods([UIButton class]) will list all the methods of classes UIButton -> UIControl->UIView->UIResponder->NSObject.
Reflection: Its the ability to add new classes and to add/modify interfaces of existing classes. It also includes the ability to modify the relationship between classes For example, the objC runtime allows new classes to be added, methods to be added to a class and instance variables to be added to a class created at runtime. It also allows the superclass of a class to be replaced by another class.
To demonstrate reflection in objC, lets add the "description" method to a class. The "description" method is a part of the NSObject protocol and it is invoked when a string format specifier "%@" pattern is used in some methods. e.g. NSString :: stringWithFormat. The NSObject class implements "description" and returns the string <className:objectPointer> where className is the name of the class to which the objectPointer refers to. Our custom description method returns the default NSObject supplied "description" string plus it also lists the instance variables of the class, their values in the specified object and ivar types.
So to test reflection, create some NSObject derived class and add some instance variables to it. Also add a method called testReflection as shown below. The test code assumes that the new class has iVars "ivarString"(NSString) and "ivarNumber" (NSUInteger). You can replace it with anything you want but make sure that the types are supported by myDescriptionIMP. The implementation currently supports only the types id and signed/unsigned versions of common scalar types. Adding support to currently unsupported types is mostly trivial. Create an object of the new class and call its testReflection method. You will see reflection in action as your custom method added at runtime is invoked.
NOTE: (null) is displayed for ivar data type if its value is nil and its an object. "type not supported" is displayed in place of ivar value if the data type of the ivar is not supported by myDescriptionIMP.
Swizzling: The term "Swizzling" in objective C refers to exchanging the implementation of two methods(class or instance) at runtime. So you apply introspection to access method implementations and reflection to actually exchange the method implementation to achieve swizzling.
The term "implementation" refers to the actual function pointer to the code(implementation) of the method. objC runtime maintains a struct called "objc_method" for each method of a class. This struct has the method name, argument and return types of that method and the "implementation" of the method. So swizzling basically involves exchanging the value the "implementation" field between the objc_method data of two different methods. It doesn't make sense in most cases to change the "type" field of this structure since that means the caller of the method would also need to change the way the method is called.
NOTE:Calling a method of an object in objectiveC involves sending a message to that object to execute the specified method with the supplied arguments. This provides the opportunity to reroute the message to another implementation of that method. This is exactly what objC runtime helps us do to realize swizzling.
We can get selectors by using @selector(XnameofselectorX). So how do we turn a selector into a method? The concept of a instance/class method only exists in the context of a class. So by using a Class object and a selector , we can derive the METHOD of that selector corresponding to that specific class. METHOD is a type which hides the underlying obj_method struct from the caller as its considered as an implementation detail.
For example, consider the interface of a class called EZObject
The following code can be used to get the selector of method1
The following code can be used to get the METHOD of that selector in EZObject.
NOTE1: class_getClassMethod is used to get the METHOD of a class method.
NOTE2: class_getInstanceMethod searches for the method in the specified class and all its super classes.
So how would a prototype of a swizzling function look like? We already know that we need a Class object and two selectors, one is the existing selector and the other is a new selector which will be swizzled with the existing one.
So a possible prototype can look like this
Now since swizzling applies to both instance method and class method, how does the caller convey that information? We can make a rule that the swizzledSelector is treated as a selector of a instance method of the class "c" . If there is no such instance method, then its treated as a selector to a class method.
Ok. Now lets go ahead and implement the function
The function method_exchangeImplementation does the actual work of exchanging the "implementation" field of the objc_method data associated with the two METHOD's.
Where can swizzling be used? You may have read in articles about objective C class categories that one disadvantage of using a category method to override an existing method is that there is no way to call the original method so you have to basically implement the functionality of the original method in the override. This is essentially true from the language syntax point of view. But what if you can make the call to the original method routed to your own implementation and from there your code calls original implementation? Swizzling allows you to do such a bait and switch trick. Calling methods of an object in objectiveC involves sending messages to that object to execute the specified method. This provides the opportunity to reroute the message to another implementation of the method
Lets say you are very much interested in logging all the different types of gesture recognizers being used by the cocoaTouch framework in your app. Wouldn't it be great if you can override UIView::addGestureRecognizer in your own category method myAddGestureRecognizer and log the presence of gesture recognizers there and still have the ability to call the UIView's implementation of that method?
The way this trick works is that you exchange the implementation of the UIView::addGestureRecognizer with your own UIView category method called myAddGestureRecognizer and then from within the implementation of myAddGestureRecognizer, call myAddGestureRecognizer! Remember that swizzling switches the implementation(function pointer) but not the name of the method. So when you are calling myAddGestureRecognizer, your message is routed to the original implementation of UIView:addGestureRecognizer.
The UIView category implementation file will look like this
In the app delegates didFinishLaunchingWithOptions method, swizzle the addGestureRecognizer method using the following code
In my app , i dropped a UITableView in a view and swizzled addGestureRecognizer as described above. This is what i got in the Log window
Author: Raj Lokanath