Friday, June 3, 2011
Key-Value Validation
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.
Friday, April 29, 2011
Delegates and Memory Management
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.
Tuesday, March 15, 2011
BYU CocoaHeads Presentations
Friday, February 25, 2011
Blog Traffic
Monday, February 21, 2011
Quick Look for iOS
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.
Thursday, August 26, 2010
Raster Map Overlays for iPhone
- Download and install GDAL (available at gdal.org)
- Download the TileMap sample project.
- Get the coordinates of your image’s bounding rect.
- Generate a .vrt file.
- Generate the mercator projected map tiles.
- Add the tiles to the sample project.
Next, download the TileMap sample project. This is available to all iOS developers through the WWDC10 website http://developer.apple.com/videos/wwdc/2010/. The sample code can be hard to find. It’s on the iTunes page where you download WWDC10 session videos. Between GDAL and this sample project, you as a developer won’t need to do much to get your map working.
This is where things get interesting. You will need to find the coordinates of the bounding rect of your image to overlay. Google Earth makes this easy. I got this tip from a fellow developer on the Apple developer forums. First, find the area you want to place the image overlay. Next, right click on ‘My Places’ (it’s on the left column). Select Add > Image Overlay. This will open a pop up window. From here you can open your image, change the opacity, and move/resize your image. Make your image transparent enough to see both your image and the underlying map. Once you have adjusted your image to match, click on the ‘Location’ tab. There you will find your latitudes/longitudes. You will need to convert those to a decimal value. A quick Google search will turn up a tool to make that conversion for you.
Now to put GDAL to work. The first part is to generate a .vrt (virtual raster) file. This is probably the hardest part of the process. It’s very easy to make a mistake. You will use gdal_translate to map your corner pixels to the coordinates you just found. The terminal command is as follows:
gdal_translate -of VRT -a_srs EPSG:4326 \
-gcp x1 y1 long1 lat1 \
-gcp x2 y2 long2 lat2 \
-gcp x3 y3 long3 lat3 \
-gcp x4 y4 long4 lat4 \
image.jpg image.vrt
There are a few things to note. First, your map image doesn’t have to be .jpg. Several formats are supported. Also, be very careful with your coordinates. Small errors make big problems. Notice that longitude comes first. I had them backwards and spent two days trying to figure out why it wouldn’t work. As for the x and y values, these are your pixel coordinates. (0,0) is the top left of the image.
If you have done everything correctly, the rest is easy. gdal2tiles.py will take your .vrt file and image overlay and generate mercator projected map tiles. The command is simple - “gdal2tiles.py -p mercator image.vrt.” You only need to give it the .vrt file. The .vrt file keeps a relative path to the image overlay. gdal2tiles.py will generate your map tiles along with other various files. If you don’t want these extra files there are command line options to skip them.
With your new map tiles, you simply need to add them to the TileMap project. First, delete the old tiles in the sample. Make sure they are deleted both in XCode and the source directory. If you have previously run the app, you may need to delete the app from your device/simulator. Next, copy your map tiles into your project. When copying your files, there is a vital gotcha to watch out for. You will need to create folder references instead of groups (see image below). Folder references will appear as a blue folder icon, whereas groups appear as a yellow folder icon. The reason for this is because the code is dependent on not just the files but also the directory structure.
If you named your map tiles directory any other than “Tiles,” you will need to change the code to use your directory instead. Finally, run the app and everything should work. If there is a problem, it likely occurred while generating your .vrt file.
Friday, August 13, 2010
Using a UITextField in a UIAlertView
Saturday, August 7, 2010
My First Quartz Composition
Quartz composer is often used for making screensavers. So, to learn QC, I decided to make a screensaver of my own. I started with a simple concept, two spheres orbiting around each other. I thought it would be simple anyway. All of the motion is accomplished through the use of LFOs (low-frequency oscillators). At first, I thought I could use three LFOs, one for each axis, to generate the desired motion. I couldn’t find anyway to get this to work. In the end, I found an alternative solution. Each sphere has two LFOs that cause the sphere to orbit in the yz-plane. From there, I used a 3D transformation patch to rotate the two spheres about the y and z axes. This achieved the movement I mentally envisioned. Much of my efforts were trial and error. There is very little documentation available for QC. I hope this to change in time.
Below is a link to my Quartz Composition for anyone interested.
http://dl.dropbox.com/u/2998162/Quartz%20Composer/Orbiting_Spheres.qtz
My First Quartz Composer Patches
This new patch suited me well. The only problem was that it required using two buttons and those buttons couldn’t be pressed simultaneously. I, of course, could do better. I added a few more components to my patch and changed it to a toggle switch. This permitted one button to toggle between the two states. I later discovered that there is a standard toggle patch, but it can toggle between any two numbers. Mine specifically uses boolean values.
Below are links to download the two patches I developed for those who are interested. Note that since these are patches, not macros, the underlying logic is not available to view.
http://dl.dropbox.com/u/2998162/Quartz%20Composer/RS_Flip_Flop.qtz
http://dl.dropbox.com/u/2998162/Quartz%20Composer/Toggle_Switch.qtz
Tuesday, July 6, 2010
C++ Templates
Today I was working on some standard C++ data structures for a CS class. I wanted to make them generic so that they could be used to hold any primitive or object. C++ offers this flexibility with templates. I found some simple tutorials and went at it. When I was finished, I couldn’t get them to work. I kept getting an undefined reference error during the linking process. What made it the most challenging is that everything was syntactically correct. I had to scour the internet to find the answer. It turns out that with C++ templates, the implementation must be included in the header file. There are a few ways to accomplish this. First, is to use the “export” keyword just before declaring the template. This option isn’t supported by most compilers. I have been using g++ which does not support this feature. Second, the .cpp file can be #included at the end of the .h file. I didn’t prefer that approach. Third, the implementation can be copied to the end of the header file. This is essentially the same as the second option. The fourth option is to simply define each method as soon as it is declared. Any of these four options will essentially merge the implementation and header files directly or indirectly. I took the last option. Once my files were merged, my data structures worked perfectly. Again, it is always the fine details that create the biggest problems.
