2
OBJECTIVE-C FOR THE LAZY

Objective-C has been met with both derision and adulation during its illustrious career. Brought to popularity by NeXTStep and inspired by the design of Smalltalk, Objective-C is a superset of C. Its most notable characteristics are the use of infix notation and absurdly long class names. People tend to either love it or hate it. People who hate it are wrong.

In this chapter, I’ll go over the basics of Objective-C, assuming that you’re already familiar with programming in some language or another. Know, however, that Cocoa and Objective-C are constantly changing. I can’t cover all of their finer details adequately in a single chapter, but I do include some hints here to help nondevelopers get their bearings when examining Objective-C code. If you’re starting from very little programming knowledge, you may wish to check out a book like Knaster, Malik, and Dalrymple’s Learn Objective-C on the Mac: For OS X and iOS (Apress, 2012) before you dig in.

As much as I’d like to stick with the most modern coding patterns of Objective-C, if you’re auditing existing code, you may come across plenty of crusty, reused code from the early days of iOS. So just in case, I’ll go over both historical Objective-C constructs and the newly sanctioned versions.

Key iOS Programming Terminology

There are a few terms you’ll want to be familiar with to understand where Apple’s various APIs come from. Cocoa is the general term for the frameworks and APIs that are used in Objective-C GUI programming. Cocoa Touch is a superset of Cocoa, with some added mobile-related APIs such as dealing with gestures and mobile GUI elements. Foundation classes are Objective-C classes that make up much of what we call the Cocoa API. Core Foundation is a lower-level C-based library upon which many Foundation classes are based, usually prefixed with CF instead of NS.

Passing Messages

The first key to grokking Objective-C is understanding that the language is designed around the concept of message passing, rather than calling. It’s useful (for me, anyway) to think of Objective-C as a language where objects sit around shouting at each other in a crowded room, rather than a language where hierarchical directors give orders to their subordinates. This analogy especially makes sense in the context of delegates, which I’ll get to shortly.

At its most basic, sending Objective-C messages looks like this:

[Object doThisThingWithValue:myValue];

That’s like saying, “Hey there, Object! Please do this thing using a value of myValue.” When passing in multiple parameters, the nature of the first one is conventionally indicated by the message name. Any subsequent parameters must be both defined as part of the class and specifically named when called, as in this example:

if (pantsColor == @"Black") {

    [NSHouseCat sleepOnPerson:person
                   withRegion:[person lap]
                  andShedding:YES
                      retries:INT_MAX];
}

In this simplified simulation of catnapping under certain conditions, sleepOnPerson specifies a place to sleep (person), and withRegion specifies the region of the person to sleep on by sending person a message returning that person’s lap. The andShedding parameter accepts a Boolean, and retries specifies the number of times this action will be attempted—in this case, up to the maximum value of an integer on a platform, which will vary depending on whether you have a 64-bit cat.

If you’ve been writing Objective-C for a while, you may notice that the formatting of this code looks different than what you’re used to. That’s because this is an arcane method of formatting Objective-C code, known as “the correct way,” with vertically aligned colons between argument names and values. This keeps the pairings between parameter names and values visually obvious.

Dissecting an Objective-C Program

The two main parts of an Objective-C program are the interface and the implementation, stored in .h and .m files, respectively. (These are roughly analogous in purpose to .h and .cpp files in C++.) The former defines all of the classes and methods, while the latter defines the actual meat and logic of your program.

Declaring an Interface

Interfaces contain three main components: instance variables (or ivars), class methods, and instance methods. Listing 2-1 is the classic (that is, deprecated) Objective-C 1.0 way to declare your interfaces.

   @interface Classname : NSParentClass {
     NSSomeType aThing;
       int anotherThing;
   }
+ (type)classMethod:(vartype)myVariable;
- (type)instanceMethod:(vartype)myVariable;
   @end

Listing 2-1: Declaring an interface, archaic version

Inside the main @interface block at , instance variables are declared with a class (like NSSomeType) or a type (like int), followed by their name. In Objective-C, a + denotes the declaration of a class method , while a - indicates an instance method . As with C, the return type of a method is specified in parentheses at the beginning of the definition.

Of course, the modern way of declaring interfaces in Objective-C is a little different. Listing 2-2 shows an example.

@interface Kitty : NSObject {
       @private NSString *name;
       @private NSURL *homepage;
       @public NSString *color;
   }

   @property NSString *name;
   @property NSURL *homepage;
@property(readonly) NSString *color;

   + (type)classMethod:(vartype)myVariable;
   - (type)instanceMethod:(vartype)myVariable;

Listing 2-2: Declaring an interface, modern version

This new class, called Kitty, inherits from NSObject . Kitty has three instance variables of different accessibility types, and three properties are declared to match those instance variables. Notice that color is declared readonly ; that’s because a Kitty object’s color should never change. This means when the property is synthesized, only a getter method will be created, instead of both a getter and a setter. Kitty also has a pair of methods: one class method and one instance method.

You may have noticed that the example interface declaration used the @private and @public keywords when declaring instance variables. Similar to other languages, these keywords define whether ivars will be accessible from within only the class that declared it (@private), accessible from within the declaring class and any subclasses (@protected), or accessible by any class (@public). The default behavior of ivars is @protected.

NOTE

Newcomers to the language often want to know whether there is an equivalent to private methods. Strictly speaking, there isn’t a concept of private methods in Objective-C. However, you can have the functional equivalent by declaring your methods only in the @implementation block instead of declaring them in both the @interface and the @implementation.

Inside an Implementation File

Just like .c or .cpp files, Objective-C implementation files contain the meat of an Objective-C application. By convention, Objective-C files use .m files, while Objective-C++ files (which mix C++ and Objective-C code) are stored in .mm files. Listing 2-3 breaks down the implementation file for the Kitty interface in Listing 2-2.

   @implementation Kitty
@synthesize name;
   @synthesize color;
   @synthesize homepage;

   + (type)classMethod:(vartype)myVariable {
       // method logic
   }

   - (type)instanceMethod:(vartype)myVariable {
       // method logic
   }
   @end

   Kitty *myKitty = [[Kitty alloc] init];

[myKitty setName:@"Ken"];
myKitty.homepage = [[NSURL alloc] initWithString:@"http://me.ow"];

Listing 2-3: A sample implementation

The @synthesize statements at create the setter and getter methods for the properties. Later, these getter and setter methods can be used either with Objective-C’s traditional infix notation , where methods of the format propertyName and setPropertyName (like name and setName, respectively) get and set values, or with dot notation , where properties like homepage are set or read using the .property format, as they might be in other languages.

NOTE

Be careful with dot notation, or just don’t use it. Dot notation makes it hard to know whether you’re dealing with an object or a C struct, and you can actually call any method with it—not only getters and setters. Dot notation is also just visually inconsistent. Long story short, in this book I’ll avoid dot notation in the name of consistency and ideological purity. But despite my best efforts, you’ll likely encounter it in the real world anyway.

Technically, you don’t need to synthesize properties that are declared in the interface file with @property, like name, color, and homepage in Listing 2-3; the compiler in recent versions of Xcode synthesizes these properties on its own. But you may want to manually declare them anyway for clarity or when you want to change the name of the instance variable to differentiate it from the property name. Here’s how manually synthesizing a property works:

@synthesize name = thisCatName;

Here, the property name is backed by the instance variable thisCatName because it was manually synthesized. However, the default behavior with automatic property synthesis is analogous to this:

@synthesize name = _name;

This default behavior prevents developers from accidentally meddling with the instance variables directly, instead of using setters and getters, which can cause confusion. For example, if you set an ivar directly, you’ll be bypassing any logic in your setter/getter methods. Automatic synthesis is probably the best way to do things, but you’ll be seeing manual synthesis in code for a long time to come, so it’s best to be aware of it.

Specifying Callbacks with Blocks

One thing that’s becoming increasingly popular in Objective-C code is the use of blocks, which are often used in Cocoa as a way to specify a callback. For example, here’s how you’d use the dataTaskWithRequest method of the NSURLSessionDataTask class:

   NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                         delegate:self
                                                    delegateQueue:nil];

   NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                           completionHandler:
     ^(NSData *data, NSURLResponse *response, NSError *error) {
               NSLog(@"Error: %@ %@", error, [error userInfo]);
        }];

The ^ at is declaring a block that will be executed once the request is complete. Note that no name is specified for this function because it won’t be called from anywhere other than this bit of code. A block declaration just needs to specify the parameters that the closure will take. From there, the rest of the block is just like a normal function. You can use blocks for tons of other things as well, but to start with, it’s probably sufficient to have a basic understanding of what they are: things that begin with ^ and do stuff.

How Objective-C Manages Memory

Unlike some other languages, Objective-C does not have any garbage collection. Historically, Objective-C has used a reference counting model, using the retain and release directives to indicate when an object needs to be freed, to avoid memory leaks. When you retain an object, you increase the reference count—that is, the number of things that want that object to be available to them. When a piece of code no longer needs an object, it sends it a release method. When the reference count reaches zero, the object is deallocated, as in this example:

NSFish *fish = [[NSFish alloc] init];
   NSString *fishName = [fish name];
[fish release];

Assume that before this code runs, the reference count is 0. After , the reference count is 1. At , the release method is called to say that the fish object is no longer needed (the application just needs the fish object’s name property), and when fish is released, the reference count should be 0 again.

The [[Classname alloc] init] can also be shortened to [Classname new], but the new method isn’t favored by the Objective-C community because it’s less explicit and is inconsistent with methods of object creation other than init. For example, you can initialize NSString objects with [[NSString alloc] initWithString:@"My string"], but there’s no equivalent new syntax, so your code would end up having a mix of both methods. Not everyone is averse to new, and it’s really a matter of taste, so you’re likely to see it both ways. But in this book, I’ll favor the traditional approach.

Regardless of which allocation syntax you prefer, the problem with a manual retain/release is that it introduced the possibility of errors: programmers could accidentally release objects that had already been deallocated (causing a crash) or forget to release objects (causing a memory leak). Apple attempted to simplify the situation with automatic reference counting.

Automatic Reference Counting

Automatic reference counting (ARC) is the modern method of Objective-C memory management. It removes the need for manually tracking reference counts by automatically incrementing and decrementing the retain count where appropriate.1 Essentially, it inserts retain and release methods for you. ARC introduces a few new concepts, listed here:

Weak and strong references assist in preventing cyclical references (referred to as strong reference cycles), where a parent object and child object both have ownership over each other and never get deallocated.

• Object ownership between Core Foundation objects and Cocoa objects can be bridged. Bridging tells the compiler that Core Foundation objects that are cast to Cocoa objects are to be managed by ARC, by using the __bridge family of keywords.

@autoreleasepool replaces the previously used NSAutoReleasePool mechanism.

In modern Cocoa applications with ARC, the details of memory management are unlikely to come into play in a security context. Previously exploitable conditions such as double-releases are no longer a problem, and memory-management-related crashes are rare. It’s still worth noting that there are other ways to cause memory management problems because CFRetain and CFRelease still exist for Core Foundation objects and C malloc and free can still be used. I’ll discuss potential memory management issues using these lower-level APIs in Chapter 11.

Delegates and Protocols

Remember how objects “shout at each other in a crowded room” to pass messages? Delegation is a feature that illustrates Objective-C’s message-passing architecture particularly well. Delegates are objects that can receive messages sent during program execution and respond with instructions that influence the program’s behavior.

To be a delegate, an object must implement some or all methods defined by a delegate protocol, which is an agreed-upon method of communication between a delegator and a delegate. You can declare your own protocols, but most commonly you’ll be using established protocols in the core APIs.

The delegates you’ll write will typically respond to one of three fundamental message types: should, will, and did. Invoke these messages whenever an event is about to happen and then let your delegates direct your program to the correct course of action.

Should Messages

Objects pass should messages to request input from any available delegates on whether letting an event happen is a good idea. Think of this as the final call for objections. For example, when a shouldSaveApplicationState message is invoked, if you’ve implemented a delegate to handle this message, your delegate can perform some logic and say something like, “No, actually, we shouldn’t save the application state because the user has checked a checkbox saying not to.” These messages generally expect a Boolean as a response.

Will Messages

A will message gives you the chance to perform some action before an event occurs—and, sometimes, to put the brakes on before it does. This message type is more like saying, “Hey guys! Just an FYI, but I’m going to go do this thing, unless you need to do something else first. I’m pretty committed to the idea, but if it’s a total deal-breaker, let me know and I can stop.” An example would be the applicationWillTerminate message.

Did Messages

A did message indicates that something has been decided for sure and an event is going to happen whether you like it or not. It also indicates that if any delegates want to do some stuff as a result, they should go right ahead. An example would be applicationDidEnterBackground. In this case, did isn’t really an indication that the application has entered the background, but it’s a reflection of the decision being definitively made.

Declaring and Conforming to Protocols

To declare that your class conforms to a protocol, specify that protocol in your @interface declaration within angle brackets. To see this in action, look at Listing 2-4, which shows an example @interface declaration that uses the NSCoding protocol. This protocol simply specifies that a class implements two methods used to encode or decode data: encodeWithCoder to encode data and initWithCoder to decode data.

@interface Kitty : NSObject <NSCoding> {
       @private NSString *name;
       @private NSURL *homepage;
       @public NSString *color;
   }

   @implementation Kitty

- (id)initWithCoder:(NSCoder *)decoder {
       self = [super init];
       if (!self) {
           return nil;
       }

       [self setName:[decoder decodeObjectForKey:@"name"]];
       [self setHomepage:[decoder decodeObjectForKey:@"homepage"]];
       [self setColor:[decoder decodeObjectForKey:@"color"]];

       return self;
   }

- (void)encodeWithCoder:(NSCoder *)encoder {
       [encoder encodeObject:[self name] forKey:@"name"];
       [encoder encodeObject:[self author] forKey:@"homepage"];
       [encoder encodeObject:[self pageCount] forKey:@"color"];
   }

Listing 2-4: Declaring and implementing conformance to the NSCoding protocol

The declaration at specifies that the Kitty class will be conforming to the NSCoding protocol.2 When a class declares a protocol, however, it must also conform to it, which is why Kitty implements the required initWithCoder and encodeWithCoder methods. These particular methods are used to serialize and deserialize objects.

If none of the built-in message protocols do what you need, then you can also define your own protocols. Check out the declaration of the NSCoding protocol in Apple’s Framework header files (Listing 2-5) to see what a protocol definition looks like.

@protocol NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;

@end

Listing 2-5: The declaration of the NSCoding protocol, from Frameworks/NSCoding.h

Notice that the NSCoding definition contains two methods that any class conforming to this protocol must implement: encodeWithCoder and initWithCoder. When you define a protocol, you must specify those methods yourself.

The Dangers of Categories

Objective-C’s category mechanism allows you to implement new methods on existing classes at runtime, without having to recompile those classes. Categories can add or replace methods in the affected class, and they can appear anywhere in the codebase. It’s an easy way to quickly change the behavior of a class without having to reimplement it.

Unfortunately, using categories is also an easy way to make egregious security mistakes. Because they can affect your classes from anywhere within the codebase—even if they appear only in third-party code—critical functionality, such as TLS endpoint validation, can be completely overridden by a random third-party library or a careless developer. I’ve seen this happen in important iOS products before: after carefully verifying that TLS/SSL works correctly in their application, developers include a third-party library that overrides that behavior, messing up their own properly designed code.

You can usually spot categories by noting @implementation directives that purport to implement classes already present in Cocoa Touch. If a developer was actually creating a category there, then the name of the category would follow the @implementation directive in parentheses (see Listing 2-6).

@implementation NSURL (CategoryName)

- (BOOL) isPurple; {
    if ([self isColor:@"purple"])
        return YES;
    else
        return NO;
}
@end

Listing 2-6: Implementing a category method

You can also use categories to override existing class methods, which is a potentially useful but particularly dangerous approach. This can cause security mechanisms to be disabled (such as the aforementioned TLS validation) and can also result in unpredictable behavior. Quoth Apple:

If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.

In other words, multiple categories can define or overwrite the same method, but only one will “win” and be called. Note that some Framework methods may themselves be implemented via a category—if you attempt to override them, your category might be called, but it might not.

A category may also accidentally override the functionality of subclasses, even when you only meant for it to add a new method. For example, if you were to define an isPurple method on NSObject, all subclasses of NSObject (which is to say, all Cocoa objects) would inherit this method. Any other class that defined a method with the same name might or might not have its method implementation clobbered. So, yes, categories are handy, but use them sparingly; they can cause serious confusion as well as security side effects.

Method Swizzling

Method swizzling is a mechanism by which you can replace the implementation of a class or instance method that you don’t own (that is, a method provided by the Cocoa API itself). Method swizzling can be functionally similar to categories or subclassing, but it gives you some extra power and flexibility by actually swapping the implementation of a method with a totally new implementation, rather than extending it. Developers typically use this technique to augment functionality of a method that’s used by many different subclasses so they don’t have to duplicate code.

The code in Listing 2-7 uses method swizzling to add a logging statement to any call of setHidden. This will affect any subclass of UIView, including UITextView, UITextField, and so forth.

   #import <objc/runtime.h>

   @implementation UIView(Loghiding)

- (BOOL)swizzled_setHidden {
       NSLog(@"We're calling setHidden now!");

     BOOL result = [self swizzled_setHidden];

       return result;
   }


+ (void)load {
       Method original_setHidden;
       Method swizzled_setHidden;

       original_setHidden = class_getInstanceMethod(self, @selector(setHidden));
       swizzled_setHidden = class_getInstanceMethod(self, @selector(swizzled_
        setHidden));
     method_exchangeImplementations(original_setHidden, swizzled_setHidden);
   }


   @end

Listing 2-7: Exchanging the implementation of an existing method and a replacement method

At , a wrapper method is defined that simply spits out an SLog that the setHidden method is being called. But at , the swizzle_SetHidden method appears to be calling itself. That’s because it’s considered a best practice to call the original method after performing any added functionality, to prevent unpredictable behavior like failing to return the type of value the caller would expect. When you call swizzled_setHidden from within itself, it actually calls the original method because the original method and the replacement method have already been swapped.

The actual swapping is done in the load class method , which is called by the Objective-C runtime when loading the class for the first time. After the references to the original and swizzled methods are obtained, the method_exchangeImplementations method is called at , which, as the name implies, swaps the original implementation for the swizzled one.

There are a few different strategies for implementing method swizzling, but most of them carry some risk since you’re mucking around with core functionality.

If you or a loved one want to implement method swizzling, you may want to consider using a fairly well-tested wrapper package, such as JRSwizzle.3 Apple may reject applications that appear to use method swizzling in a dangerous way.

Closing Thoughts

Overall, Objective-C and the Cocoa API are nicely high-level and prevent a number of classic security issues in C. While there are still several ways to mess up memory management and object manipulation, most of these methods result in a denial of service at worst in modern code. If you’re a developer, rely on Cocoa as much as possible, rather than patching in C or C++ code.

Objective-C does, however, contain some mechanisms, such as categories or swizzling, that can cause unexpected behavior, and these mechanisms can affect your codebase widely. Be sure to investigate these techniques when you see them during an app assessment because they can potentially cause some serious security misbehavior.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.56.194