How to get internals of Objective-C class

DISCLAIMER: This post describes APIs which reference internal methods and could be considered as private API and, therefore, apps could be rejected by Apple’s App Store review team. There is even a special message from Apple to avoid using these techniques in production apps.

There are situations when you want to understand more how your code works. So, you might be interested in getting all the information about particular class, including its instance variables, private methods and so on. There is even one particular reason, you might do that – to avoid accidentally overriding internal methods.

So, let’s create a method which will describe any object. We’re looking for output similar to regular class declaration.

@interface OurClass : ParentClass <Protocols...>
{
    // instance variables
}

// properties, class and instance methods

@end

This declaration will contain definition of all derived methods, variables, including private ones.

First, let’s check what API will help us here.

Class objectClass = [self class];

NSUInteger classCount, methodCount, ivarCount, protocolCount, propertyCount;

Method *classMethods = class_copyMethodList(object_getClass(objectClass), &classCount);
Method *methods = class_copyMethodList(objectClass, &methodCount);
Ivar *ivars = class_copyIvarList(objectClass, &ivarCount);
Protocol * __unsafe_unretained *protocols = class_copyProtocolList(objectClass, &protocolCount);
objc_property_t *properties = class_copyPropertyList(objectClass, &propertyCount);

Objective-C runtime provides us with functions to get method list, ivar (instance variable) list, protocol list and property list of any class. However, we need to get object’s class by calling -class method on it.

Also, note that we can get class method list by using the same API’s, but, we need to get class’ class (which is called metaclass).

And one more thing on these APIs. This is plain C functions. You have to manage (free) memory yourself.

Okay. We have the list of all information on the class. But these functions will return only data which was declared in this class. It does not return you derived data. You have to go up the class tree yourself.

do
{
    // get information about current class
    // ...

    // get to super class until we end up with NSObject
    objectClass = [objectClass superclass];
} while (objectClass);

Having this information, we can construct human-readable descriptions. There are some more APIs to help us with that. For example, NSStringFromClass() and NSStringFromSelector() are very useful. You could check out example source code to get more details on introspection API.

I’d like to mention one thing. These C functions encode data types via C string, so we have to decode it.

-(NSString*)stringForType:(const char*)type parentheses:(BOOL)parentheses
{
    NSString *typeDescription;
    switch (type[0])
    {
        case '@':
        {
            if (strlen(type) > 1)
            {
                typeDescription = [[NSString stringWithFormat:@"%s *", (type + 1)] stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            }
            else
            {
                typeDescription = @"id";
            }
            break;
            
        }
            
        case 'c':
            typeDescription = @"char";
            break;
            
        case 'i':
            typeDescription = @"int";
            break;
            
        case 's':
            typeDescription = @"short";
            break;
            
        case 'l':
            typeDescription = @"long";
            break;
            
        case 'q':
            typeDescription = @"long long";
            break;
            
        case 'C':
            typeDescription = @"unsigned char";
            break;
            
        case 'I':
            typeDescription = @"unsigned int";
            break;
            
        case 'S':
            typeDescription = @"unsigned short";
            break;
            
        case 'L':
            typeDescription = @"unsigned long";
            break;
            
        case 'Q':
            typeDescription = @"unsigned long long";
            break;
            
        case 'f':
            typeDescription = @"float";
            break;
            
        case 'd':
            typeDescription = @"double";
            break;
            
        case 'B':
            typeDescription = @"bool";
            break;
            
        case '*':
            typeDescription = @"char *";
            break;
            
        case '#':
            typeDescription = @"Class";
            break;
            
        case ':':
            typeDescription = @"SEL";
            break;
            
        case 'v':
            typeDescription = @"void";
            break;
            
        case '[':
        case '{':
        case '(':
            typeDescription = [NSString stringWithFormat:@"%s", type];
            break;
            
        case 'b':
            typeDescription = [NSString stringWithFormat:@"%s", type];
            break;
            
        case '^':
            typeDescription = [NSString stringWithFormat:@"%@ *", [self stringForType:(type + 1) parentheses:NO]];
            break;
            
        default:
            typeDescription = [NSString stringWithFormat:@"?%s", type];
            break;
    }
    return parentheses ? [NSString stringWithFormat:@"(%@)", typeDescription] : typeDescription;
}

Example application does decrypt most of those, but not all. Apple provides documentation on encoding details. You should also understand that not all definitions are compileable. This is just for your information, for better understanding of internal structure.

Example source code creates a category for NSObject that adds -objectDescription method, which returns desired object description. Also, each line has a comment with name of class in which method/property/ivar was lastly declared (basically, this means that if you call this method, it will use implementation of that particular class, even if it was declared higher up in hierarchy).

Example will provide result similar to this.

2013-05-31 13:28:37.141 Introspector[79715:c07] this is:
@interface INViewController : UIViewController<UITextInput_Internal, NSCoding, NSObject, UIAppearanceContainer>
{
    UIView * _containerViewInSheet; // UIViewController
    id _dropShadowView; // UIViewController
    {CGSize="width"f"height"f} _formSheetSize; // UIViewController
    char _ignoreAppSupportedOrientations; // UIViewController
    int _lastKnownInterfaceOrientation; // UIViewController
    NSNumber * _key; // INViewController
    float _customNavigationInteractiveTransitionDuration; // UIViewController
    int _modalTransitionStyle; // UIViewController
    {CGSize="width"f"height"f} _contentSizeForViewInPopover; // UIViewController
    UINavigationItem * _navigationItem; // UIViewController
    int _retainCount; // UIViewController
    float _customNavigationInteractiveTransitionPercentComplete; // UIViewController
    int _keyInt; // INViewController
    UIBarButtonItem * _editButtonItem; // UIViewController
    UIPopoverController * _popoverController; // UIViewController
    NSArray * _storyboardSegueTemplates; // UIViewController
    UIView * _savedFooterSuperview; // UIViewController
    UIResponder * _defaultFirstResponder; // UIViewController
    NSString * _unretained; // INViewController
    UIStoryboard * _storyboard; // UIViewController
    UIViewController * _childModalViewController; // UIViewController
    UITabBarItem * _tabBarItem; // UIViewController
    NSArray * _internalArray; // INViewController
    NSString * _title; // UIViewController
    NSString * _storyboardIdentifier; // UIViewController
    Class isa; // NSObject

    // many more skipped ...
}

+({objc_method_description=:*} *) methodDescriptionForSelector:(SEL); // NSObject
+(Class)class; // NSObject
+(id)copy; // NSObject
+(id) _createOtherValueSetterWithContainerClassID:(id) key:(id); // NSObject
+(void)_stopTrackingObjectsWithIdentifiers; // UIResponder
// many more skipped ...

@property (readonly, nonatomic) char _isPresentedFormSheet; // UIViewController
@property (nonatomic, ivar=_modalPresentationStyle) int modalPresentationStyle; // UIViewController
@property (copy, ivar=_key) NSNumber * key; // INViewController
@property (copy, nonatomic, ivar=_customNavigationTransitionCompletion) ? * customNavigationTransitionCompletion; // UIViewController
@property (nonatomic, ivar=_customNavigationInteractiveTransitionDuration) float customNavigationInteractiveTransitionDuration; // UIViewController
@property (readonly, nonatomic, ivar=_savedHeaderSuperview) UIView * savedHeaderSuperview; // UIViewController
@property (readonly) UIView * inputView; // UIResponder
// many more skipped ...

-(void)_supportedInterfaceOrientationsDidChange; // UIViewController
-(char) validateValue:(?N^@) forKey:(id) error:(?o^@); // NSObject
-(char) canHandleSnapbackIdentifier:(id) animated:(char); // UIViewController
-(void)_updateLayoutForStatusBarAndInterfaceOrientation; // UIViewController
-(void) _setDisallowMixedOrientationPresentations:(char); // UIViewController
-(char)isBeingDismissed; // UIViewController
-(void) presentModalViewController:(id) animated:(char); // UIViewController
-(void) setAccessibilityViewIsModal:(char); // NSObject
-(id)__autorelease_OA; // NSObject

-(void) attentionClassDumpUser:(id) yesItsUsAgain:(id) althoughSwizzlingAndOverridingPrivateMethodsIsFun:(id) itWasntMuchFunWhenYourAppStoppedWorking:(id) pleaseRefrainFromDoingSoInTheFutureOkayThanksBye:(id); // UIViewController
// many more skipped ...

@end

Take a note that BOOL data type is shown as char (which is true for Objective-C runtime).

And remember, you should be using this only to avoid breaking internal APIs, not to use them in production app.

Apple documentation: Objective-C runtime reference, type encodings.

Source code is available on GitHub under terms of MIT License.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s