Class Posing!

Discussion in 'iOS Development' started by lauNchD, Dec 19, 2008.

  1. lauNchD

    lauNchD Well-Known Member

    Joined:
    Jan 27, 2008
    Messages:
    1,844
    Likes Received:
    261
    Device:
    iPhone 5 (Black)
    Introduction

    I found out a new way to modify/override Objective-C classes! It's (in my opinion) a lot simpler than MobileSubstrate (but is probably worse on the performance aspect) because we can simply make a subclass of any class and redirect all calls to the (super)class to the subclass. In addition, you can easily use it in your SDK apps internally (for instance for debugging purposes), or make a dylib loadable by MobileSubstrate.

    Tutorial

    Sounds complicated, huh? Actually, it's easy! Let me show you by creating a simple subclass of NSObject that prints out a log message every time an instance is initialized and deallocated. Then, we will make it pose as NSObject.

    Code:
    @interface VerboseObject : NSObject
    { /* You CANNOT set any new instance variables! Posing will FAIL and your app will CRASH! If you need to store temporary data somewhere, use static class-level variables or some shared instance of NS(Mutable)Dictionary */ }
    @end
    
    @implementation VerboseObject
    
    - (id) init
    {
    if (self = [super init])
    NSLog(@"Object %@ initializing!", self); // an NSLogged NSObject is displayed with its address and class name, an NSString is displayed with its contents
    
    return self;
    }
    
    - (void) dealloc
    {
    NSLog(@"Deallocating object %@ !", self);
    [super dealloc];
    }
    
    @end
    
    If you're halfway experienced, nothing new here! But what's the point? How do you pose? By adding this simple line:
    Code:
    [VerboseObject poseAsClass: [NSObject class]];
    You should add this in before the class is used, probably as one of the first things into your main() function or dylib inititializer. After this line of code, every NSObject subclass will be a VerboseObject subclass. As you know, in a full-fledged application, many objects/instances inheriting from NSObject are created, and together they all will spam your log

    Please Register or Log in to view images

    ! (Talk about thousands of messages...)

    NOTES

    This tutorial is just a proof of concept. Actually, it's a stupid idea to pose in place of NSObject, but it could be useful for debugging. This works for nearly any class, so you could also override SpringBoard classes with this (as done by QuickGold, SBSettings, ...).

    A few things to keep in mind if you want to pose:

    • You need to make a direct/concrete subclass pose as its superclass.
    • You can't declare any new ivars! Since we need to make Objective-C believe that the subclass actually is the superclass, it can't take up more memory than it's supposed to.
    • Do NOT try to pose while your app is already up and running. It'll cause a great amount of confusion, since you can't just change the class of an instance when it's already been initialized. Put the posing code in your main() function or initializer before UIApplicationMain()!
    I hope this could help some of you!

    Cheers,
    lauNchD.

    (Ihr k├Ânnt auch auf Deutsch antworten wenn ihr wollt...)

    Please Register or Log in to view images

  2. SkylarEC

    SkylarEC Super Moderator Emeritus Staff Member

    Joined:
    Sep 19, 2007
    Messages:
    6,642
    Likes Received:
    129
    Nah, it's easier to just use Objective-C runtime. Then you can actually do things that you want. And you can do all that within your application without breaking Apple's rules. I mean, I'm pretty sure they don't want you injecting code, and whatnot, but I use Objective-C runtime to manipulate UIKit objects to mess with things I'm "not supposed" to mess with.

    For example, I take the UINavigationController's build in UITransitionView and add animations to its animation key array. That way I can get past the default 8 default animation types that the UINavigationController will handle by default.

    Also, to get an ivar, just do something like this:
    Code:
    Ivar ivar = class_getInstanceVariable((id)class, "_anIvar");
  3. lauNchD

    lauNchD Well-Known Member

    Joined:
    Jan 27, 2008
    Messages:
    1,844
    Likes Received:
    261
    Device:
    iPhone 5 (Black)
    I think you kind of got me wrong. In your posing subclass, you can access all ivars from the superclass/-instance. You just can't *declare* new ones.
    BTW: I also use the Objective-C runtime for things "i'm not supposed to do." To test out some undocumented classes/functions/features without writing an @interface declaration, I do the following:
    Code:
    [window addSubview:(UIView*)objc_msgSend(NSClassFromString(@"UIFontChooser"), @selector(sharedFontChooser))];
    [I'm typing this from my iPod right now so I can't really answer anything]

Share This Page