Have you ever gone away from your computer, played around with a feature on your application, and found a performance problem you wanted to dig into at that point?

Some of the tools we use to debug these issues, such as Instruments, are powerful enough that I wouldn’t blame them for needing a separate machine to use them.

However, others don’t have the same excuse — such as very useful render debugging options (such as color blending layers or color off-screen rendering). Although these are enabled with Xcode, they are essentially just toggling a flag on the device itself, so it seems reasonable that we could remove Xcode from the image entirely.

Note: The timing of this article may seem a bit off, as staying away from the computer is now even less feasible than usual.

By way of comparison, I’d like to mention that WWDC is less than a week away, and I think publishing this post now slightly increases the chances that it will be officially endorsed by bad luck and become redundant — which is still a good result overall.

CARenderServerSetDebugOption

When you switch in the Xcode render debug options, one of the final result is CARenderServerSetDebugOption calls on your device or emulator. This function takes three arguments:

CARenderServerSetDebugOption(
    0,    /* render server mach port */
    0x02, /* debug option (Color Blended Layers) */
    1     /* new value */
)
Copy the code

CARenderServerSetDebugOption by rendering server communication to work with iOS, this is a separate process, processing… Well, render.

The first parameter here is a Mach port that allows other processes to communicate with the server. You can call the corresponding port CARenderServerGetServerPort myself, but you can also through 0 have CARenderServerSetDebugOption see it for you.

The remaining two arguments are fairly simple — we pass an integer value that represents the option we want to update, followed by its new value (0 or 1).

Here are the options Xcode allows you to toggle:

Color Blended Layers:              0x02
Color Hits Green and Misses Red:   0x13
Color Copied Images:               0x01
Color Layer Formats:               0x14
Color Immediately:                 0x03
Color Misaligned Images:           0x0E
Color Offscreen-Rendered Yellow:   0x11
Color Compositing Fast-Path Blue:  0x12
Flash Updated Regions:             0x00
Copy the code

Set options from the emulator

Because CARenderServerSetDebugOption not public function, we need to find it at runtime. We can use DLSYM to do this, which allows us to find the address of a symbol in a given dynamic library (in this case, QuartzCore) :

// The location of the QuartzCore // framework on iOS let quartzCorePath = "/System/Library/Frameworks/" + "QuartzCore.framework/QuartzCore" // Use `dlopen` to get a handle // for the QuartzCore framework let quartzCoreHandle =  dlopen( quartzCorePath, RTLD_NOW) // Store the address of our function let functionAddress = dlsym( quartzCoreHandle, "CARenderServerSetDebugOption") // Create a typealias representing our // function's param types / return type typealias  functionType = @convention(c) ( CInt, CInt, CInt ) -> Void // Cast the address to the // above function type let CARenderServerSetDebugOption = unsafeBitCast( functionAddress, to: functionType.self) // Call the function! CARenderServerSetDebugOption(0, 0x2, 1)Copy the code

If you call the above code in the application running on the emulator, you will see the color blending layer option in effect and your application is now drawn in a sea of red and green.

Use CARenderServerSetDebugOption top the list of other value to replace the second parameter in call allows you to switch to other options – including usually only for equipment not simulator public option (although not all can be perfect operation).

Set options from the device

right

The code above runs on a physical device without any errors or logs — but it has virtually no impact. That’s because call this function must contain com binary files. Apple. QuartzCore. Right to debug.

It’s easy to add this permission to use on jailbroken devices using Ldid or JTool, but unfortunately this does limit our ability to use this feature on standard devices.

Note: You can also use rights to bypass here, but I’m not having much luck with this particular right. Part of the problem may be that I still don’t know exactly where to actually check this right – another problem.

Create a preference pack

Since we’re basically limited to jailbroken devices here, we might as well switch to controlling it through Settings, as it feels more natural than leaving some random application to toggle rendering options.

PreferenceLoader provides an easy way to do this; It allows us to create our own preference pane and have full control over the view controller it displays, which is perfect for our use case.

We can start by creating a subclass PSListController, provided by iOS’ Preferences. Framework. This gives us a lot of convenience, including the ability to easily create new switch units using PSSpecifier instances, each of which describes a personal preference.

For example, the following Settings create a preferences pane with a single switch and the ability to run arbitrary code when toggling that switch:

#import <Preferences/PSListController.h>
#import <Preferences/PSSpecifier.h>

// Create a `PSListController` subclass
@interface ExampleListController: PSListController

@end

@implementation ExampleListController

// Override `viewDidLoad` to setup our specifiers,
// which describe each preference we support
- (void)viewDidLoad {
    [super viewDidLoad];

    // Specifier for a switch labeled "Basic Switch"
    // that calls our setter below when toggled
    PSSpecifier *specifier =
        [PSSpecifier preferenceSpecifierNamed:@"Basic Switch"
                                       target:self
                                          set:@selector(valueChanged:forSpecifier:)
                                          get:nil
                                       detail:Nil
                                         cell:PSSwitchCell
                                         edit:Nil];

    // Update our controller's specifiers list
    self.specifiers = [NSMutableArray arrayWithObject:specifier];
}

// Setter called when our switch is toggled
- (void)valueChanged:(NSNumber *)value forSpecifier:(PSSpecifier *)specifier {
    // Logs "Specifier 'Basic Switch' changed to 1" (or 0)
    NSLog(@"Specifier '%@' changed to %i",
        specifier.name,
        value.boolValue);
}

@end
Copy the code

From here, we can easily PSSpecifier for each want to render the debug options to create a public, then call CARenderServerSetDebugOption our setter to enable this option. We can even add a getter for CARenderServerGetDebugOption ensure our preferences pane in restarts always reflect the current state.

The final result

With this setting, we were able to build the full preferences pane, and I was very pleased with the end result:

Bryce, co/on device – r…

If you’re interested in the generated code, or want to try it out on your own device, you can find the project on GitHub.

Recommended at the end of the article: iOS popular anthology

IOS Interview Basics (part 1)

IOS Interview Basics (part 2)

IOS Interview Basics (part 3)

IOS Interview Basics (4)

IOS Interview Basics (5)