One of the most common design patterns is the singleton. However, implementing singletons, especially in Objective-C, can be very repetitive. I often forget how to properly override all the memory management methods and need to look up a previous singleton. One of the goals of programming is to automate the repetetive, mundane tasks. Cocoa With Love has a great synthesized singleton made from a C pre-processor macro. The problem with this is that it's not flexible. So, I made an Objective-C singleton that can be dropped in any project. By subclassing RBSingleton you can gain all the benefits of a singleton without adding any further code and you have full flexibility to customize each subclass.
There are two main parts that make RBSingleton possible. First, it stores all the singleton instances in a class-wide dictionary. A singleton typically keeps a static pointer to the singleton instance. This works fine as long as it's not subclassed. When subclasses become involved, the subclass that allocates first defines the singleton instance for all subclasses. This restriction is normally beneficial to singletons, but breaks with subclassing. I could have required subclasses to provide a pointer to a static class pointer through a method call, but this imposes more on the subclass's implementation than I wanted. By using a class-wide dictionary, every subclass can store its singleton instance under a unique key. To guarantee a unique key, I simply use the subclass's name.
Second, the singleton instance is allocated by dynamically calling NSObject's -allocWithZone: method as shown below.
Method allocMethod = class_getClassMethod([NSObject class], @selector(allocWithZone:));
sharedInstance = [method_invoke(self, allocMethod, nil) initialize];
The trick here was to allocate the singleton as the class of the RBSingleton subclass but use NSObject's -allocWithZone: method to do so. This is similar to calling [super allocWIthZone:nil];. The difference is that it's jumping more than one level in the inheritance hierarchy. This is known as a grandsuper call. The Objective-C dynamic runtime makes it possible to make such a call. You will also notice that I hardcoded [NSObject class] into the code. I could have dynamically discovered the root class with the following code:
+ (Class)rootClass {
Class rootClass = nil;
Class currentClass = [self class];
while ((currentClass = class_getSuperclass(currentClass)))
rootClass = currentClass;
return rootClass;
}
If I instead needed to get a grandsuper class that isn't necessarily the root class and I know a subclass of the grandsuper I could use simpler code like this:
[RBSingleton superclass];
Since I know the specific grandsuper class I need, I hardcoded it for efficiency. If you use RBSingleton and find a need to dynamically discover a particular grandsuper class, you can use the above code to do so. I've uploaded RBSingleton as a Gist and you can find it here.
One last note, to use RBSingleton you need to include libobjc.dylib for the dynamic runtime calls.
Saturday, July 30, 2011
Tuesday, July 26, 2011
#define Abuse
Lately I've been encountering some terrible Objective-C code, and I have a few words to say about it. One of the first problems I've found is the overuse of #define. Don't get me wrong, #define has its place, but when it comes to constants #define should not be the first choice.
Anti-Patterns
One of the problems with #define is that it's simply a macro substitution. There is generally no type associated with the value. This makes is harder for the compiler to warn you of potential errors. Macros can also have side effects you may forget about. For example:
#define MY_STRING_CONSTANT [[NSString alloc] initWithFormat:@"Hello World!"] // Very bad.
The above example will cause a memory leak unless you remember to release the value each time. Just a side note, Apple's static analyzer will pick up this error if you turn it on. I should also mention that the following, related example is not preferable. I have actually seen this used in production code before.
#define MY_STRING_CONSTANT [NSString stringWithFormat:@"Hello World!"] // Still not great.
Numbers
With a couple anti-patterns out of the way, let's go over the proper way to define constants. You will find that it doesn't take much more effort to properly define constants. Numbers are the simplest and one of the most common, so we'll start with them. In your .m file (applies to .c or .cpp too) you will define your constant like the following:
const NSInteger kMyInt = -100;
const NSUInteger kMyUnsignedInt = 42;
const CGFloat kMyFloat = 3.14;
You could instead use 'int', 'unsigned int', and 'float' if you prefer, but the above types are generally the preferred data types. If you want to keep your constant visible to just your implementation file, then you are done. If not, you need to do one last thing. In your header file, you will need to use the following:
extern const NSInteger kMyInt;
extern const NSUInteger kMyUnsignedInt;
extern const CGFloat kMyFloat;
The reserved word 'extern' makes a promise to the compiler. You are letting the compiler know that you are not defining the value right away, but at link time, that value will exist. In this case, it's defined in your implementation file. With that in place, you are now done; your constant is defined and other classes can #import (#include for C and C++) your header file and use your number constants.
You may wonder why you can't define the constant right in your header file. If you do, you will get an error message that looks like this: "ld: duplicate symbol <Constant Name> in <File 1> and <File 2>." Generally #import handles duplicate imports properly; however, it doesn't work for constants. I figure this error is why developers unaware of 'extern' default to #define.
Enumerations
Closely related to numbers are enumerations. If you ever have a series of related number constants, you probably want an enum. For example:
// A contrived example but should get the idea across.
enum {
kJanuary = 1,
kFebruary = 2,
kMarch = 3,
kApril = 4,
kMay = 5,
kJune = 6,
kJuly = 7,
kAugust = 8,
kSeptember = 9,
kOctober = 10,
kNovember = 11,
kDecember = 12,
};
Enumerations are great for bit masks too.
// Another contrived example.
enum {
kBit1 = 1 << 0,
kBit2 = 1 << 1,
kBit3 = 1 << 2,
kBit4 = 1 << 3,
};
Enumerations are where type checking really helps. Say you are using a switch statement, the compiler can warn you if you didn't include one of your enum values as a switch case.
Strings
Strings are also very common in Cocoa programming. There is one extra detail that makes them different from numbers. At first you may want to make a constant like this:
const NSString * kMyString = @"Hello World!"; // Wrong - String Anti-Pattern 1
This is wrong for a couple different reasons. First, NSString is immutable, meaning that it can't be changed. This essentially makes it a constant by default. However, there is a big flaw with the above anti-pattern. I will first show the proper way:
NSString * const kMyString = @"Hello World!"; // Right
At first glance, you are probably wondering why 'const' is next to the constant name. The difference between the number and string examples is that the NSString is a pointer. The first string example tells the system that the value @"Hello World!" can't change, but the pointer value can change. You probably wouldn't, but you could do the following:
kMyString = @"Goodbye World!"; // Very bad, but allowed in String Anti-Pattern 1.
You didn't change the value of @"Hello World!", but you did change what kMyString pointed to. That doesn't sound like a constant at all.
By the way, the following is also permissible, but wrong.
const NSString * const kMyString = @"Hello World!"; // Wrong - String Anti-Pattern 2
This makes both the value and the pointer constant. However, the first 'const' changes the data type of the constants. None of the Cocoa frameworks accept a const NSString * (since it's redundant). This means that you will have a compiler warning anytime you try to pass your constant to a Cocoa framework method. You might do something similar with other objects, but in Objective-C, such cases should be very rare. This is something more common in C or C++.
Objects
Objects other than strings can be a little more tricky. You can't do the following:
MyObject * const kObject = [[MyObject alloc] init]; // Wrong - Object Anti-Pattern 1
The problems is the right-hand side does not evaluate to a compile time constant. Furthermore, if it was possible, it would be difficult to configure the object properly. This requires a different technique.
// Right
+ (MyObject *)MyConstObject {
static MyObject * obj = nil;
if (!obj) {
obj = [[MyObject alloc] init];
// Any other initial setup.
}
return obj;
}
Looking at the return type of MyObject *, you may think this has the same problem that String Anti-Pattern 1 had. This is not the case here. The original constant pointer is protected within the method; it can't be changed externally. When you call the method, you receive a copy of the object's address. You can change it if you want, but it won't affect anyone else. Naively you may want to change the return type to MyObject * const, but a const pointer doesn't affect the data type. You could do the following regardless:
MyObject * obj = [[self class] MyConstObject]; // Type MyObject * is the same as MyObject * const
One last note about object constants is that they are technically memory leaks. However, they are permissible for the same reason that singletons are permissible. What's more interesting is that Apple's latest static analyzer now flags singletons as memory leaks but still doesn't catch the above constant.
When to use #define
I should probably include a kind word about #define. If you want to use any pre-processor macros such as #if or #ifdef, then #define is your only choice. #define is also good for simple, inline functions or when you want to write some code that will do automatic renaming (see Cocoa With Love's synthesized singleton). #define has countless valuable uses, but when it comes to constants, try something else first.
Anti-Patterns
One of the problems with #define is that it's simply a macro substitution. There is generally no type associated with the value. This makes is harder for the compiler to warn you of potential errors. Macros can also have side effects you may forget about. For example:
#define MY_STRING_CONSTANT [[NSString alloc] initWithFormat:@"Hello World!"] // Very bad.
The above example will cause a memory leak unless you remember to release the value each time. Just a side note, Apple's static analyzer will pick up this error if you turn it on. I should also mention that the following, related example is not preferable. I have actually seen this used in production code before.
#define MY_STRING_CONSTANT [NSString stringWithFormat:@"Hello World!"] // Still not great.
Numbers
With a couple anti-patterns out of the way, let's go over the proper way to define constants. You will find that it doesn't take much more effort to properly define constants. Numbers are the simplest and one of the most common, so we'll start with them. In your .m file (applies to .c or .cpp too) you will define your constant like the following:
const NSInteger kMyInt = -100;
const NSUInteger kMyUnsignedInt = 42;
const CGFloat kMyFloat = 3.14;
You could instead use 'int', 'unsigned int', and 'float' if you prefer, but the above types are generally the preferred data types. If you want to keep your constant visible to just your implementation file, then you are done. If not, you need to do one last thing. In your header file, you will need to use the following:
extern const NSInteger kMyInt;
extern const NSUInteger kMyUnsignedInt;
extern const CGFloat kMyFloat;
The reserved word 'extern' makes a promise to the compiler. You are letting the compiler know that you are not defining the value right away, but at link time, that value will exist. In this case, it's defined in your implementation file. With that in place, you are now done; your constant is defined and other classes can #import (#include for C and C++) your header file and use your number constants.
You may wonder why you can't define the constant right in your header file. If you do, you will get an error message that looks like this: "ld: duplicate symbol <Constant Name> in <File 1> and <File 2>." Generally #import handles duplicate imports properly; however, it doesn't work for constants. I figure this error is why developers unaware of 'extern' default to #define.
Enumerations
Closely related to numbers are enumerations. If you ever have a series of related number constants, you probably want an enum. For example:
// A contrived example but should get the idea across.
enum {
kJanuary = 1,
kFebruary = 2,
kMarch = 3,
kApril = 4,
kMay = 5,
kJune = 6,
kJuly = 7,
kAugust = 8,
kSeptember = 9,
kOctober = 10,
kNovember = 11,
kDecember = 12,
};
Enumerations are great for bit masks too.
// Another contrived example.
enum {
kBit1 = 1 << 0,
kBit2 = 1 << 1,
kBit3 = 1 << 2,
kBit4 = 1 << 3,
};
Enumerations are where type checking really helps. Say you are using a switch statement, the compiler can warn you if you didn't include one of your enum values as a switch case.
Strings
Strings are also very common in Cocoa programming. There is one extra detail that makes them different from numbers. At first you may want to make a constant like this:
const NSString * kMyString = @"Hello World!"; // Wrong - String Anti-Pattern 1
This is wrong for a couple different reasons. First, NSString is immutable, meaning that it can't be changed. This essentially makes it a constant by default. However, there is a big flaw with the above anti-pattern. I will first show the proper way:
NSString * const kMyString = @"Hello World!"; // Right
At first glance, you are probably wondering why 'const' is next to the constant name. The difference between the number and string examples is that the NSString is a pointer. The first string example tells the system that the value @"Hello World!" can't change, but the pointer value can change. You probably wouldn't, but you could do the following:
kMyString = @"Goodbye World!"; // Very bad, but allowed in String Anti-Pattern 1.
You didn't change the value of @"Hello World!", but you did change what kMyString pointed to. That doesn't sound like a constant at all.
By the way, the following is also permissible, but wrong.
const NSString * const kMyString = @"Hello World!"; // Wrong - String Anti-Pattern 2
This makes both the value and the pointer constant. However, the first 'const' changes the data type of the constants. None of the Cocoa frameworks accept a const NSString * (since it's redundant). This means that you will have a compiler warning anytime you try to pass your constant to a Cocoa framework method. You might do something similar with other objects, but in Objective-C, such cases should be very rare. This is something more common in C or C++.
Objects
Objects other than strings can be a little more tricky. You can't do the following:
MyObject * const kObject = [[MyObject alloc] init]; // Wrong - Object Anti-Pattern 1
The problems is the right-hand side does not evaluate to a compile time constant. Furthermore, if it was possible, it would be difficult to configure the object properly. This requires a different technique.
// Right
+ (MyObject *)MyConstObject {
static MyObject * obj = nil;
if (!obj) {
obj = [[MyObject alloc] init];
// Any other initial setup.
}
return obj;
}
Looking at the return type of MyObject *, you may think this has the same problem that String Anti-Pattern 1 had. This is not the case here. The original constant pointer is protected within the method; it can't be changed externally. When you call the method, you receive a copy of the object's address. You can change it if you want, but it won't affect anyone else. Naively you may want to change the return type to MyObject * const, but a const pointer doesn't affect the data type. You could do the following regardless:
MyObject * obj = [[self class] MyConstObject]; // Type MyObject * is the same as MyObject * const
One last note about object constants is that they are technically memory leaks. However, they are permissible for the same reason that singletons are permissible. What's more interesting is that Apple's latest static analyzer now flags singletons as memory leaks but still doesn't catch the above constant.
When to use #define
I should probably include a kind word about #define. If you want to use any pre-processor macros such as #if or #ifdef, then #define is your only choice. #define is also good for simple, inline functions or when you want to write some code that will do automatic renaming (see Cocoa With Love's synthesized singleton). #define has countless valuable uses, but when it comes to constants, try something else first.
Labels:
Best Practices,
C,
C++,
Objective-C
Tuesday, July 19, 2011
Driving Technical Change Review
One particular book that caught my eye some time ago is Driving Technical Change by Terrence Ryan. In his book, Ryan teaches the skills necessary to convince others to adopt your ideas. Though the book focuses around technical fields, most of the skills taught apply to other disciplines.
Summary
Ryan begins his book by classifying different types of people, such as the Cynic, the Uniformed, and the Irrational. From there he progresses to the skills needed to use on the different categories including delivering your message, gaining trust, and getting publicity. Ryan then concludes with how to strategically employ the needed skills.
Pros
The book is written in a way that it can in whatever order fits your needs. Once you have identified the category (or categories) of people you are working with, you can jump straight to the recommended skills and techniques. The chapters are short and easy to read. Every skeptic category and rhetoric technique includes short examples to illustrate the ideas.
Most of the ideas covered seem common sense. Yet there are some examples that I had never thought of employing. For example, Ryan suggests one way to get coworkers to accept a framework you've built is to open source it. If many people start using it, or even contributing to it, then your coworkers will be much more open to using your framework.
Cons
Ryan tends to dodge confronting the Irrational. It is true that little can be done to convince an irrational person. However, he doesn't offer many suggestions besides avoid them and/or have management mandate a policy. Management mandate may be the silver bullet, but what about the situation when management is irrational? Perhaps the best solution at that point is to find a new job.
Before reading this book, I was expecting Ryan to go into more detail on leadership skills. However, many skills such as gaining trust are left with few examples or explanation of how to gain trust. I do like the succinctness of Ryan's writing, but sometimes I'm left wanting more concrete examples.
Recommendation
If you are one of the many that feel your voice isn't heard, this book likely has some skills you haven't tried yet. Ryan shares his secret to leadership: "you can be promoted to management, but no one appoints you a leader." Driving Technical Change can help you take initiative and become a leader.
You can purchase Driving Technical Change here from The Pragmatic Bookshelf.
Summary
Ryan begins his book by classifying different types of people, such as the Cynic, the Uniformed, and the Irrational. From there he progresses to the skills needed to use on the different categories including delivering your message, gaining trust, and getting publicity. Ryan then concludes with how to strategically employ the needed skills.
Pros
The book is written in a way that it can in whatever order fits your needs. Once you have identified the category (or categories) of people you are working with, you can jump straight to the recommended skills and techniques. The chapters are short and easy to read. Every skeptic category and rhetoric technique includes short examples to illustrate the ideas.
Most of the ideas covered seem common sense. Yet there are some examples that I had never thought of employing. For example, Ryan suggests one way to get coworkers to accept a framework you've built is to open source it. If many people start using it, or even contributing to it, then your coworkers will be much more open to using your framework.
Cons
Ryan tends to dodge confronting the Irrational. It is true that little can be done to convince an irrational person. However, he doesn't offer many suggestions besides avoid them and/or have management mandate a policy. Management mandate may be the silver bullet, but what about the situation when management is irrational? Perhaps the best solution at that point is to find a new job.
Before reading this book, I was expecting Ryan to go into more detail on leadership skills. However, many skills such as gaining trust are left with few examples or explanation of how to gain trust. I do like the succinctness of Ryan's writing, but sometimes I'm left wanting more concrete examples.
Recommendation
If you are one of the many that feel your voice isn't heard, this book likely has some skills you haven't tried yet. Ryan shares his secret to leadership: "you can be promoted to management, but no one appoints you a leader." Driving Technical Change can help you take initiative and become a leader.
You can purchase Driving Technical Change here from The Pragmatic Bookshelf.
Labels:
Book Review,
Management,
Pragmatic Bookshelf
Tuesday, June 21, 2011
Designed For Use Review
I've been anticipating the release of Designed for Use: Create Usable Interfaces for Applications and the Web for a while now. I was a little disappointed that it was delayed by almost a week from its original release date. Nevertheless, I bought it and finished it right away.
Summary
Lukas Mathis breaks the design process into three distinct parts: research, design, and implementation. Each category is subdivided into smaller steps classified as either idea-based or skill-based. The progression is logical, but also is written in a way that steps can be skipped or read out of order according to the needs of the product.
Pros
When I first saw the book, I expected a focus strictly on what Mathis terms the "design phase" (Part 2 of 3). Design to me, before reading this book, meant wireframes, prototypes, and usability testing. The book gives much more. Mathis shows that the design process extends throughout the entire development cycle.
Designed for Use includes countless links to other resources. Mathis cites many prominent UX and Design blogs, books, videos, etc. Most are web based, so they are freely and widely available.
Many great techniques are suggested that I never thought of as part of "design." Mathis includes mock press releases, job shadowing, and feature sorting. Not only does he teach several techniques, but he also gives low-budget suggestions. He removes any excuse for not following certain steps such as usability testing.
Cons
Overall the book seems to have a bias towards desktop and web applications. There are several references to developing mobile applications, but there are few resources mentioned. I was expecting to see services such as Flurry and TestFlight included in the sections on user feedback and testing. Nevertheless, the ideas and techniques still apply.
Recommendation
In general product development I often see two areas that lack: design and documentation. Mathis hits on both topics. Every step of the design process is covered succinctly and thoroughly making it quick and easy to read yet includes many references for further indulgence. I would highly recommend this book to both young and experienced developers. Designed for Use can be purchased here on pragprog.com.
Summary
Lukas Mathis breaks the design process into three distinct parts: research, design, and implementation. Each category is subdivided into smaller steps classified as either idea-based or skill-based. The progression is logical, but also is written in a way that steps can be skipped or read out of order according to the needs of the product.
Pros
When I first saw the book, I expected a focus strictly on what Mathis terms the "design phase" (Part 2 of 3). Design to me, before reading this book, meant wireframes, prototypes, and usability testing. The book gives much more. Mathis shows that the design process extends throughout the entire development cycle.
Designed for Use includes countless links to other resources. Mathis cites many prominent UX and Design blogs, books, videos, etc. Most are web based, so they are freely and widely available.
Many great techniques are suggested that I never thought of as part of "design." Mathis includes mock press releases, job shadowing, and feature sorting. Not only does he teach several techniques, but he also gives low-budget suggestions. He removes any excuse for not following certain steps such as usability testing.
Cons
Overall the book seems to have a bias towards desktop and web applications. There are several references to developing mobile applications, but there are few resources mentioned. I was expecting to see services such as Flurry and TestFlight included in the sections on user feedback and testing. Nevertheless, the ideas and techniques still apply.
Recommendation
In general product development I often see two areas that lack: design and documentation. Mathis hits on both topics. Every step of the design process is covered succinctly and thoroughly making it quick and easy to read yet includes many references for further indulgence. I would highly recommend this book to both young and experienced developers. Designed for Use can be purchased here on pragprog.com.
Labels:
Book Review,
Design,
Pragmatic Bookshelf,
UX
Tuesday, June 7, 2011
Git Repos
I've recently created a few Git repos on Github. So far I have included a bug reporting tool, a file previewer, and a load of handy categories. I will also be moving my samples to Github. Check them out at https://github.com/rob-brown.
Friday, June 3, 2011
Key-Value Validation
Along with all the other features of Key-Value Coding, there are methods in the API for validating the properties of an object. These methods are known as Key-Value Validation (KVV). The format for a validation method is -(BOOL)validate<Key>:(id*)error: (NSError**) where “Key” is the name of the property to validate. You must be sure to spell the property name properly, otherwise the validator may not be called properly. When calling a validator, you have two choices. First you can call the validator directly. Second, you can call -(BOOL)validateValue:(id*)forKey:(NSString*)error:(NSError**). The second choice will infer the name of the validator method, which is why it is important to spell the method name properly.
The following gives a simple template of how to implement a validator. This template may be copied directly into Xcode.
- (BOOL)validate<#Key#>:(id *)ioValue error:(NSError **)outError {
if (<#Value is not valid#>) {
// Try to coerce the value into a valid value.
// The coerced value must be autoreleased.
*ioValue = <#Some valid value#>;
if (<#Value cannot be coerced#>) {
if (error != NULL) {
// Create an error object.
// Merge errors if necessary.
}
return NO;
}
}
return YES;
}
Implementing validators has several gotchas to watch for. One of the things to notice is the value being passed in for validation is of type id*. This means that you can return a new value by reference. For example, if you want to ensure that a name is unique, you can use the validator to append a number to the name to coerce it to be unique.
Coercing a value has some critical memory management issues. The value passed in for validation must be autoreleased, and a coerced value, if any, must likewise be autoreleased. Furthermore, if you do return a coerced value it must be return a new value rather than modifying the old value, even if the old value is mutable.
When implementing a validator you must never call -set<Key> within the validator. The validator is responsible for validating, not setting.
Primitives cannot be validated since the validators expect an object. However, you may wrap the primitive value in an NSValue or NSNumber object.
Another gotcha to note is that validators are not called automatically. It is up to your discretion when you want your objects to be validated. When calling a validator, there is a general form to follow as shown below:
// Note that the value is autoreleased.
NSString * someName = [[[NSString alloc] initWithFormat”@“Timmy”] autorelease];
NSError * error = nil;
if ([obj validateName:&someName error:&error) {
// The setter is called since the value could have been coerced and to properly retain the value.
[obj setName:someName];
}
else {
// Report an error to the user or whatever else you want.
}
Aside from the above template, you could validate the parameters of your Objective-C setters by using NSParameterAssert([obj validateValue:&someValue forKey:@“someKey” error:NULL]). This is good for debugging since asserts can be disabled by defining NS_BLOCK_ASSERTIONS.
Core Data extends the ability of KVV. Although KVC does not automatically call validation methods, Core Data provides three points which validators can be called automatically. These methods are -(BOOL)validateForInsert:(NSError**), -(BOOL)validateForUpdate:(NSError**), and -(BOOL)validateForDelete:(NSError**). These methods are called when an NSManagedObject is inserted into a managed object context, updated, or deleted, respectively. You may override these methods in your subclasses of NSManagedObject to call your validators. Additionally, you may perform inter-property validation here. There may be instances where certain combinations of values are incorrect. For example, in a family tree, it is incorrect to set a female person as someone’s father.
When using the Core Data validation methods, you must be careful to not recursively dirty the managed object context. Dirtying the MOC can be done in numerous ways such as coercing property values on every validation request. If you do recursively dirty the MOC, then you will get a message like this: ‘Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.’ If you face this situation, you will either need to find another way to implement your validator or don’t have your validator called automatically. If you opt to not call your validator automatically, then you will probably want to call your validator anytime you try to set the associated property.
In order to avoid your validators from being called automatically you have a few options. You could avoid calling NSManagedObject’s -validateForX: methods (where ‘X’ is ‘Insert’, ‘Update’, or ‘Delete’) by not calling [super validateForX:&error]. However, this may not be wise since NSManagedObject may do more than call each of the validators. You could instead try having your object reject one or more validation messages from itself. The easiest and best option is to make a validator that is not KVC-compliant. In other words, name the validator method so it doesn’t match with a property name.
When using the Core Data validation methods, you may have multiple errors occur. You have two options to handle this. First, you can return the first error as soon as it is reached. Second, you can merge the errors into a single error with error code NSValidationMultipleErrorsError.
KVV is very useful, especially for Core Data. I’m surprised it isn’t used more frequently. However, Core Data does automatically generate some basic KVV methods from the MOM file. Hopefully, this tutorial will increase the custom usage of KVV.
The following gives a simple template of how to implement a validator. This template may be copied directly into Xcode.
- (BOOL)validate<#Key#>:(id *)ioValue error:(NSError **)outError {
if (<#Value is not valid#>) {
// Try to coerce the value into a valid value.
// The coerced value must be autoreleased.
*ioValue = <#Some valid value#>;
if (<#Value cannot be coerced#>) {
if (error != NULL) {
// Create an error object.
// Merge errors if necessary.
}
return NO;
}
}
return YES;
}
Implementing validators has several gotchas to watch for. One of the things to notice is the value being passed in for validation is of type id*. This means that you can return a new value by reference. For example, if you want to ensure that a name is unique, you can use the validator to append a number to the name to coerce it to be unique.
Coercing a value has some critical memory management issues. The value passed in for validation must be autoreleased, and a coerced value, if any, must likewise be autoreleased. Furthermore, if you do return a coerced value it must be return a new value rather than modifying the old value, even if the old value is mutable.
When implementing a validator you must never call -set<Key> within the validator. The validator is responsible for validating, not setting.
Primitives cannot be validated since the validators expect an object. However, you may wrap the primitive value in an NSValue or NSNumber object.
Another gotcha to note is that validators are not called automatically. It is up to your discretion when you want your objects to be validated. When calling a validator, there is a general form to follow as shown below:
// Note that the value is autoreleased.
NSString * someName = [[[NSString alloc] initWithFormat”@“Timmy”] autorelease];
NSError * error = nil;
if ([obj validateName:&someName error:&error) {
// The setter is called since the value could have been coerced and to properly retain the value.
[obj setName:someName];
}
else {
// Report an error to the user or whatever else you want.
}
Aside from the above template, you could validate the parameters of your Objective-C setters by using NSParameterAssert([obj validateValue:&someValue forKey:@“someKey” error:NULL]). This is good for debugging since asserts can be disabled by defining NS_BLOCK_ASSERTIONS.
Core Data extends the ability of KVV. Although KVC does not automatically call validation methods, Core Data provides three points which validators can be called automatically. These methods are -(BOOL)validateForInsert:(NSError**), -(BOOL)validateForUpdate:(NSError**), and -(BOOL)validateForDelete:(NSError**). These methods are called when an NSManagedObject is inserted into a managed object context, updated, or deleted, respectively. You may override these methods in your subclasses of NSManagedObject to call your validators. Additionally, you may perform inter-property validation here. There may be instances where certain combinations of values are incorrect. For example, in a family tree, it is incorrect to set a female person as someone’s father.
When using the Core Data validation methods, you must be careful to not recursively dirty the managed object context. Dirtying the MOC can be done in numerous ways such as coercing property values on every validation request. If you do recursively dirty the MOC, then you will get a message like this: ‘Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.’ If you face this situation, you will either need to find another way to implement your validator or don’t have your validator called automatically. If you opt to not call your validator automatically, then you will probably want to call your validator anytime you try to set the associated property.
In order to avoid your validators from being called automatically you have a few options. You could avoid calling NSManagedObject’s -validateForX: methods (where ‘X’ is ‘Insert’, ‘Update’, or ‘Delete’) by not calling [super validateForX:&error]. However, this may not be wise since NSManagedObject may do more than call each of the validators. You could instead try having your object reject one or more validation messages from itself. The easiest and best option is to make a validator that is not KVC-compliant. In other words, name the validator method so it doesn’t match with a property name.
When using the Core Data validation methods, you may have multiple errors occur. You have two options to handle this. First, you can return the first error as soon as it is reached. Second, you can merge the errors into a single error with error code NSValidationMultipleErrorsError.
KVV is very useful, especially for Core Data. I’m surprised it isn’t used more frequently. However, Core Data does automatically generate some basic KVV methods from the MOM file. Hopefully, this tutorial will increase the custom usage of KVV.
Labels:
Core Data,
iOS,
Key-Value Coding,
Key-Value Validation,
KVC,
KVV,
Objective-C
Friday, April 29, 2011
Delegates and Memory Management
The iOS SDK uses the delegate pattern everywhere. Occasionally, you may want to create your own delegate protocol for some custom object. When doing so, keep in mind the subtleties of memory management. Always use @property (nonatomic, assign) rather than @property (nonatomic, retain) with delegates. It is often habit to use “retain,” but a delegate must use “assign.”
Consider the following scenario. When creating an object, its delegate is often set to “self.” Using retain as the setter policy will cause the object to retain itself. The problem is that objects (usually) don’t ever call release on delegates. Hypothetically, if a delegate is released, it’s likely to be done in -dealloc. Since the object retained itself, -dealloc will never be called. The result is a memory leak. Xcode’s static analyzer will not be able to catch this type of leak. One of the major pitfalls of reference counting is circular dependencies. The only solutions in iOS are to avoid such dependencies or force the cycle to be broken somewhere. The first option is highly preferred.
By using assign, we avoid a circular dependency. However, this doesn’t mean we can’t retain a delegate if needed. It just has to be done manually. For example: [self setDelegate:[aDelegate retain]];. This also means that the release must be done manually. Typically this can be done in -dealloc. Additionally, you may want to include a check for delegate != self. Sending [self release] in -dealloc will result in an infinite loop.
Consider the following scenario. When creating an object, its delegate is often set to “self.” Using retain as the setter policy will cause the object to retain itself. The problem is that objects (usually) don’t ever call release on delegates. Hypothetically, if a delegate is released, it’s likely to be done in -dealloc. Since the object retained itself, -dealloc will never be called. The result is a memory leak. Xcode’s static analyzer will not be able to catch this type of leak. One of the major pitfalls of reference counting is circular dependencies. The only solutions in iOS are to avoid such dependencies or force the cycle to be broken somewhere. The first option is highly preferred.
By using assign, we avoid a circular dependency. However, this doesn’t mean we can’t retain a delegate if needed. It just has to be done manually. For example: [self setDelegate:[aDelegate retain]];. This also means that the release must be done manually. Typically this can be done in -dealloc. Additionally, you may want to include a check for delegate != self. Sending [self release] in -dealloc will result in an infinite loop.
Labels:
Best Practices,
Delegate,
iOS,
Memory Management,
Objective-C
Tuesday, March 15, 2011
BYU CocoaHeads Presentations
Recently, I have added links to my presentations I have given for the BYU CocoaHeads. You can find them in the right-hand column of my blog. Among the presentations, I have include Grand Central Dispatch, Core Data, and Quick Look for iOS. Each of the presentations are shared via iWork.com. If there is interest, I may also add links for my sample projects.
Friday, February 25, 2011
Blog Traffic
Ever since I blogged about raster map overlays for iOS back in August, I have been getting some substantial traffic. In fact, my raster map post shows up as the first three results on Google. If you don’t believe me, do a Google search for “WWDC10 Tilemap.” Regardless of the high traffic, no one has left any comments. My goal is to help answer many iOS developers’ questions. So far I have been using Blogger analytics to gauge what my readers want. Analytics can only do so much though. If a post helped you or didn’t answer what you were looking for, let me know. I would love your feedback.
Monday, February 21, 2011
Quick Look for iOS
Many apps work directly with files. Quick Look is the perfect framework for previewing those files. Right out of the box, it supports all the common file formats. Like many of Apple’s other frameworks, it takes very few lines of code to add. Quick Look has been in Mac OS X for a while now and has recently been made available for iOS in 4.0.
Adding Quick Look is a few easy steps:
Next, setup a QLPreviewControllerDataSource and QLPreviewControllerDelegate. QLPreviewControllerDataSource needs to know how many QLPreviewItems you want to show (numberOfPreviewItemsInPreviewController:) and which QLPreviewItem is at each index (previewController:previewItemAtIndex:). These are often one line if you already have your documents in an array. Once the delegate and data source are set up, all you need to do is present the view however you like. The most convenient way on iPhone is [self presentModalViewController:previewController animated:YES];. QLPreviewControllerDelegate adds some extra callbacks that may be helpful, but it may be unnecessary depending on your needs.
Quick Look also comes with other features for free. First, it presents the option to open the file in another app that accepts that file format. For example, CSV files can be opened in Numbers and PDFs can be opened in iBooks. Second, files can be printed. If you don’t have a printer to test it on, Apple provides Printer Simulator in the developer tools.
Simple table views works well for managing file previews; however, you may want a more custom UI and more control. UIDocumentInteractionController is the class to use for that. I won’t go into details on document interaction. Session 106 of WWDC 2010 goes into great detail about UIDocumentInteractionController. The sample code is also available in the developer docs.
EDIT:
Since writing this post I have written a subclass of QLPreviewController that includes many conveniences, and have removed the old sample code. If you are using my old Quick Look sample, you should switch over to RBFilePreviewer. You can find it here on Github. You can also find a demo of RBFilePreviewer in my demo repository.
Adding Quick Look is a few easy steps:
- Import QuickLook.framework.
- Create a QLPreviewItem (or NSURL).
- Create a QLPreviewControllerDataSource.
- Create a QLPreviewControllerDelegate.
- Present a QLPreviewController.
Next, setup a QLPreviewControllerDataSource and QLPreviewControllerDelegate. QLPreviewControllerDataSource needs to know how many QLPreviewItems you want to show (numberOfPreviewItemsInPreviewController:) and which QLPreviewItem is at each index (previewController:previewItemAtIndex:). These are often one line if you already have your documents in an array. Once the delegate and data source are set up, all you need to do is present the view however you like. The most convenient way on iPhone is [self presentModalViewController:previewController animated:YES];. QLPreviewControllerDelegate adds some extra callbacks that may be helpful, but it may be unnecessary depending on your needs.
Quick Look also comes with other features for free. First, it presents the option to open the file in another app that accepts that file format. For example, CSV files can be opened in Numbers and PDFs can be opened in iBooks. Second, files can be printed. If you don’t have a printer to test it on, Apple provides Printer Simulator in the developer tools.
Simple table views works well for managing file previews; however, you may want a more custom UI and more control. UIDocumentInteractionController is the class to use for that. I won’t go into details on document interaction. Session 106 of WWDC 2010 goes into great detail about UIDocumentInteractionController. The sample code is also available in the developer docs.
EDIT:
Since writing this post I have written a subclass of QLPreviewController that includes many conveniences, and have removed the old sample code. If you are using my old Quick Look sample, you should switch over to RBFilePreviewer. You can find it here on Github. You can also find a demo of RBFilePreviewer in my demo repository.
Labels:
iOS,
Quick Look
Subscribe to:
Posts (Atom)