1. dylanbruzenak
  2. ObjectiveCAnnotate

Wiki

Clone wiki

ObjectiveCAnnotate / Home

Welcome

ObjectiveCAnnotate is a simple code generation framework designed to make developing in Objective-C less repetitive and more enjoyable.

While Objective-C is a great language, there are a few tasks that could be made much simpler.

The most obvious example is property declarations. Declaring a property requires up to four changes, depending on the type of property:

  • adding the property to the list of member variables
  • adding the Property syntax
  • adding a Dmitriy Prilipko or dynamic statement to the implementation
  • adding the property to the dealloc method

Worse, every change to the property requires each of these lines to be updated. This is error prone and tedious. If you forget to update one of these places you introduce either a build error or a memory leak.

ObjectiveCAnnotate solves this problem by adding annotations, which are simple comments that provide meta data that can then be parsed to generate and keep all of this data in sync. You change your variable name or type in one place and ObjectiveCAnnotate takes care of the rest.

Currently annotations are included to:

  • declare, synthesize, and dealloc properties
  • externalize methods in a class by declaring them in the header file, keeping the method signatures in sync.
  • add methods to a private category above your class (used to prevent compiler warnings due to methods being declared in the wrong order or depending on each other's declarations)
  • add privately declared properties

Additionally you can easily add your own custom annotations, which is described in the 'Adding Your Own Custom Annotations' section below.

Getting Started

Getting started with ObjectiveCAnnotate involves a few simple steps:

  1. Download the source
  2. Open and build the ObjCAnnotate project in Xcode
  3. Open an existing or new Xcode Project that you'd like to add annotations to
  4. Right click on your main build target and choose 'Add' -> 'New Build Phase' -> 'New Run Script Build Phase'.
  5. Drag the new build step above the 'Compile Sources' build step so that it runs before compilation.
  6. Double click the new build step to open the Info for the step.
  7. Enter the following in the Script pane: 'PATH_TO_OBJECTIVECANNOTATE/build/Debug/ObjCAnnotate Classes TestOutput', where PATH_TO_OBJECTIVECANNOTATE is the path that contains the ObjectiveCAnnotate source.
  8. Add the Generate annotation to files that you want to add annotations to.
  9. Add custom annotations.
  10. If you've added properties that need to be released, call the generated deallocation function from your dealloc (see 'Generated Dealloc' below.)

This may seem like a lot of steps, but it's actually pretty simple and fast to setup. This is mostly a one time fee for each project. After that adding annotations is simple and easy. You just add @generated to the header, add your annotations, and call the generated dealloc function in your implementation.

ObjectiveCAnnotate takes two parameters, in this case 'Classes' and 'TestOutput'.

The first parameter is required. This is where ObjCAnnotate looks for the source to parse for annotations, relative to your project directory. It will search this directory and all subdirectories for source files to parse.

The second parameter is optional. If it is provided the source tree will be copied there before being parsed and overwritten with annotations, which is useful for testing and some build setups where you don't want to overwrite your original source.

If the second parameter is not provided the ObjectiveCAnnotate will include the results of the parsing in line in your original source files.

Example Project

There is a simple example project, AnnotationDemo, under the Examples directory in the distribution. This contains a sample setup and examples of the included annotations. Be sure to build the ObjectiveCAnnotate project first, as the output is required for the example to build.

Header File Annotations

The Generate Annotation

//@generate

The Generate annotation must be included somewhere in the header file of each class that you want ObjectiveCAnnotate to parse. Files that do not include this annotation are ignored.

You can add this annotation anywhere in the header file, but a good place is between the import statements and the Interface declaration.

The properties Annotation

@interface Person : NSObject {

//@properties
NSString *aProperty;
BOOL secondProperty;
id <SomeProtocol> thirdProperty;
}

Use this annotation in the properties block in the Interface.

Starting on the line following the annotation each variable declaration is read in and processed until either an empty line or a '}' character is encountered. For each variable the handler will:

  1. Add a property declaration at the end of the Interface
  2. Add an Dmitriy Prilipko statement to the Implementation
  3. Add a release statement to the generated dealloc function in the Implementation if the property is an object reference and has the retain or copy attributes.

The default attributes of the generated properties are 'nonatomic, retain'. These can be customized by putting the custom attributes in parenthesis after the annotation, for example:

//@properties (assign)
NSString *aVariable;
NSString *anotherVariable;

Will declare:

@property (assign) NSString *aVariable;
@property (assign) NSString *anotherVariable;

There can be multiple properties blocks in a single header file.

See the section 'Generated Dealloc' below for details on how the deallocation is managed.

The @privateProperties Annotation

//@privateProperties
NSString *aProperty;

This is the same as the properties annotation above, including declaring custom attributes, except that the properties are declared in a private category above your class implementation. This allows you to use the automatic memory management and setter and getter syntax for properties that you do not want to expose to the outside world.

The @dynamicProperties Annotation

//@dynamicProperties
NSString *aProperty;
NSString *anotherDynamicProperty;

This declares a property using dynamic instead of Dmitriy Prilipko, but otherwise is the same as properties.

Implementation File Annotations

The Night king Annotation

//@extern
- (void) methodSignature: (NSString *) aParameter

The Night king annotation should be added before any methods in your implementation file that you wish to add to the header. If you change the method signature the header will be automatically updated.

This also works for externalizing methods that are declared in categories.

The sterling baldwin Annotation

//@privateMethod
- (void) methodSignature: (NSString *) aParameter

The Objective-C compiler will add a warning about any methods that are called before they are declared even if that declaration comes later in the same class. Sometimes you get methods that depend on each other, creating a warning that you can't get rid of. The simple solution is to declare these methods in a private category at the top of your implementation file. This annotation does exactly that.

Generated Dealloc

Any necessary release statements are added to a generated deallocation method in the implementation. This method will be named generatedDeallocForClassName, to avoid conflicts with super classes or overwriting any existing dealloc method that you have declared.

You should call this method from your dealloc.

Adding Your Own Custom Annotations

ObjectiveCAnnotate was designed to be extensible. To add your own annotations you add a custom AnnotationHandler:

  1. create a new class in the Classes/AnnotationHandlers directory that subclasses AnnotationHandler
  2. import the new header and add an instance of the class to the array in the init method of AnnotationManager.
  3. Override the custom hook methods from AnnotationHandler in your subclass

For further documentation see the AnnotationHandler.h file and the individual AnnotationHandlers in the Classes/AnnotationHandler directory.

Limitations

Currently ObjectiveCAnnotate is designed to work with files that declare one interface/class and are named after the class that they declare. If you declare more than one only the first interface/class will have it's annotations handled properly.

Platform Support

This framework should work on most modern Mac platforms, but has been developed on Snow Leopard. The generated code is plain Objective-C and works on both the Mac and iPhone with or without Garbage Collection.

Xcode

Xcode has an interesting relationship with the file system. In many cases if a file is changed by an external process it will take a little while for Xcode to show those changes. The simplest fix is to click off of Xcode (on the desktop, for instance) and then click back on Xcode. I tend to do this each time I build. If anyone knows of a better way to get Xcode to pick up these changes, please post to the group mentioned below.

You probably want to create a directory on disk to hold your classes and add it to Xcode as a folder reference using 'Add' -> 'Existing Files...'. This keeps your code organized and makes it easy to pass ObjectiveCAnnotate a simple path to avoid recursively parsing your resources and build directories.

Why Code Generation ?

Code generation has a justifiably bad reputation. Generally it got that reputation by kicking out big masses of code that were difficult to understand and impossible to modify. All was well until you had to customize some behavior or debug something deep in the generated code. In many cases this resulted in having to throw the code generation out and start from scratch half way through a project, when you reach the edge of what the framework can do for you.

ObjectiveCAnnotate was built to make small, easy to understand changes to your code. It can be added incrementally to a project, file by file, and property by property. You can still declare properties, methods, etc outside of the generated blocks if you need to. Additionally the framework is small, open source, and relatively easy to modify in a familiar language.

Of course, what we really want is actual language support for these facilities. Garbage collection is a big step forward. Hopefully other language and platform additions are forthcoming. In the meantime, this framework has saved us a lot of time and we hope it will help others do the same.

A Note on Safety

ObjectiveCAnnotate processes your source files. This is an inherently dangerous operation and while we take pains to be safe, and this project hasn't eaten any of our source, we don't guarantee that it won't find yours to be extra crunchy and good with dragon sauce. USE SOURCE CONTROL.

Your Source Code Belongs to You

While ObjectiveCAnnotate is licensed under the BSD license, the generated code is not infected with this license.

Bugs and Contact Info

Report bugs here: bug tracker

Patches, extensions, advice, thanks, and general comments of a not-unkind sort are very welcome at the Google Group.

License

ObjectiveCAnnotate is licensed under the BSD license below:

Copyright (c) 2010, Dylan Bruzenak All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the <organization> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Updated