Well, we need to explain our source code style guides so that people can
follow ;-)
Well, all this is rather boring and hard to write down. After all it is pretty much
obvious how proper sourcecode formatting is to be done ;-) Anyway, we are pretty religious about formatting, so if you plan to provide patches, please follow the (absolutely incomplete) guide or prepare to get reformatted.
General Sourcecode Procedures
a) we do not use CVS commit logs. Instead we use ChangeLog files. Please document every change you do.
b) we increase library and bundle subminor versions on every change. Whether you are "just" changing the makefile or just run indent on the sources -
always increase the subminor version prior a commit.
Because you need to document your change because of a), you should always include
the new version in the ChangeLog.
Sample ChangeLog
a single change:
2003-11-29 Helge Hess <helge.hess@opengroupware.org>
* WEClientCapabilities.m: added Morgul as a known (WebDAV) user agent
(v4.2.264)
multiple changes:
2003-11-28 Helge Hess <helge.hess@skyrix.com>
* v4.2.263
* WebDAV/SoWebDAVRenderer.m: subminor cleanups
* WebDAV/SoObjectWebDAVDispatcher.m: unescape destination URL pathes
for MOVE/COPY operations (related to bug 456)
Your gain: ChangeLogs are processed periodically by the ChangeBlogger, so
its quite easy to track changes:
OGo ChangeBLog
Version Files
You should place the version of your library in a separate Version (Make)File
and include that file in your project makefile. Check out NGObjWeb for an
example of that. Our section looks like:
include ../common.make
include ./Version
The Version file looks like this:
# $Id: Version,v 1.63 2003/12/03 16:48:37 helge Exp $
SUBMINOR_VERSION:=266
# Requirements:
# v4.2.177 => NGExtensions v4.2.33
Note that we also track dependencies in that file (eg starting with libNGObjWeb
v4.2.177, NGObjWeb depends on at least NGExtensions v4.2.33).
Objective-C
So many things to write down, so little time, ...
Tabs vs Spaces, Line-Width
We never use physical tabs for obvious reasons ;-) - so set your editor to produce spaces instead of tabs. Tabwidth is 2 chars. Source code is max 80
chars in width, wrapping is forbidden. We have at a single space in most
locations, some examples:
@interface MyClass : NSObject < NSCoding >
- (id)blah;
Note that a space is between colon, class and superclass as well as between
the brackets and the protocol.
#include vs #import
We always use #include, never #import - except for Foundation (because that is not
properly guarded on MacOSX). All headers need to be properly guarded using #ifdef's.
Class Definitions
Place the @interface in a properly guarded headerfile and the @implementation in a
.m file. Exception: if the class is completely private (eg usually WOComponent
subclasses), you can include at the head of the .m file. But: if you do, please still
do include separation of header and implementation.
At the @end of the @implementation we add the classname in a comment.
Header Sample
// $Id$
#ifndef __NGObjWeb_WOResponse_H__
#define __NGObjWeb_WOResponse_H__
#import <Foundation/NSString.h>
#include <NGObjWeb/WOMessage.h>
...
/*
WOResponse
This WOMessage subclass add functionality for HTTP responses, mostly
the HTTP status. WOResponse also provides some methods for zipping itself.
*/
@class NSData;
@class WORequest;
@interface WOResponse : WOMessage < WOActionResults >
{
unsigned int status;
}
/* HTTP */
- (void)setStatus:(unsigned int)_status;
- (unsigned int)status;
@end
#endif /* __NGObjWeb_WOResponse_H__ */
Note the guard. Note that #import is used for Foundation includes and #include for
own ones. Note that we do not include the complete Foundation.h header, just the
stuff we need - other required classes are just declared using @class.
Implementation Sample
// $Id$
#include <NGObjWeb/WOResponse.h>
...
#include "common.h"
@implementation WOResponse
...
static unsigned int OWMinimumZipSize = 1024;
...
+ (WOResponse *)responseWithRequest:(WORequest *)_request {
...
- (id)init {
...
- (void)dealloc {
...
[your stuff: accessors, actions, operations]
...
/* description */
- (NSString *)description {
...
}
@end /* WOResponse */
We always use a common.h file, so that we can catch differences between
Foundations in a central position. We place class "global" statics (usually default
configurations) just after the @implementation.
The method sequence is:
+initialize / +version
Constructors (+ methods)
Init Methods (- init*)
-dealloc Method
accessor methods
other stuff
action methods
description method
Types
Please use proper style for pointers:
NSString *tmp; // CORRECT
NSString* wrong; // WRONG
Our approach is obviously correct, since in C the pointer specifier '*' is variable
specific ;-)
Methods
Various things, braces, variables. For braces we place the opening brace on the same
line if the method name fits on a single line, otherwise we place it on the next line.
For parameters we use the "_var", "var_" and "_var_" idea - for input, output and
in/out parameters (so you almost always deal with "_var" parameters.
- (void)appendString:(NSString *)_str {
}
- (void)appendString:(NSString *)_str
escapeHTML:(BOOL)_doEscape
{
}
Note that we do not align wrapped names at the colon, but rather indent the
follow-ups by 2 spaces in the next line. We prepend return type definitions with a
single space.
We always declare the return-type:
- doIt; // WRONG
- (id)doIt; // RIGHT
Class Versions
We use class versions to work around the fragile base class issue. Notably in
Objective-C we "only" have that for changing ivar layouts. You can use the
+initialize method to check whether the superclass is of the proper class:
+ (int)version {
return [super version] + 0 /* v2 */;
}
+ (void)initialize {
NSAssert2([super version] == 2,
@"invalid superclass (%@) version %i !",
NSStringFromClass([self superclass]), [super version]);
}
Note that our class version changes if a superclass of us changes because we
add up. We add the current version we think we are at in the comment.
ivars
We always refer to ivars using self-> to make it obvious that we are
talking about ivars.
self->name = [_name copy];
This places a copy of the _name parameter into the name instance variable.
-init Methods
We always check the return value of the super-init method, to allow the super method
to replace or nil the object.
- (id)init {
if ((self = [super init])) {
[self setStatus:200];
...
}
return self;
}
RETAIN/RELEASE Macros
We deprecated the use of RETAIN/RELEASE macros which are plain ugly and are only
useful in the Boehm-GC context which isn't used in OGo anyway (mostly because we
want to stay portable to Cocoa).
So while we still use RETAIN/RELEASE macros, we are slowly phasing that out:
self->person = RETAIN(_person); // WRONG
self->person = [_person retain]; // correct
ASSIGN(self->person, _person); // correct
Note that we suggest using ASSIGN and ASSIGNCOPY! Also note that you should
always copy basetypes like NSString's, NSNumber's or NSData's to make use of
the advantages of the immutable classes.
Categories
When writing categories, we use the Classname+Categoryname.m pattern for naming
the files. Eg:
@implementation NSString(HTMLEscaping)
...
@end /* NSString(HTMLEscaping) */
This should end up in a file called NSString+HTMLEscaping.m. Note that
the @end is commented because it is an @end of a @implementation section.
Variable Declarations
Stuff should be properly aligned:
int minimumActiveSessionsCount;
NSString *name;
NSString *path;
WORequestHandler *defaultRequestHandler;
If you only have a really short section and one variable has a really long type name,
non-aligned stuff is acceptable as well:
- (void)doThis {
WORequestHandler *rh;
int status; }
but this should be limited to really short ones.
Note that sourcecode should be compatible with gcc 2.95 - so no C++ style inline declarations!
- (void)doIt {
int i;
for (i ...)
int j; // WRONG: move up to the i
for (j ...)
}
Comments
Hm, where do we use comments? For one we use comments to separate
method sections, eg:
/* localization */
- (void)setLanguages:(NSArray *)_langs;
- (NSArray *)languages;
/* notifications */
- (void)awake;
- (void)sleep;
In pratice we almost never need to use comments to describe what a method
does - the method name should clearly express that, which is pretty easy using
ObjC keyword selectors.
Anyway, if it is required, we add the comments in the implementation of the
method:
- (void)setLanguages:(NSArray *)_languages {
/* this method sets the language-array of the receiver */ // useless ...
}
But we *do* recommend to comment any form of hack in an extensive way.
Describe non-obvious sections in detail!, like:
- (BOOL)allowDeletePropertiesOnNewObjectInContext:(WOContext *)_ctx {
NSString *ua;
ua = [[_ctx request] headerForKey:@"user-agent"];
if ([ua hasPrefix:@"Evolution"]) {
/* if Evo creates tasks, it tries to delete some props at the same time */
return YES;
}
return NO;
}
Public "methods" are those declared in the @interface section. If we need
forward declarations of private methods, we add an informal protocol in front
of the @implementation, like:
@interface WEClientCapabilities(Privates)
- (id)initWithRequest:(WORequest *)_request;
@end
What you want to comment are the so called "designated initializers" (read
ObjC documentation on that ;-). We seldom do this, as the DA is usually
obvious, but in case:
- (id)initWithGCCollectSize:(unsigned)_size; /* designated initializer */
Private Methods
You probably want to have "private" methods. In practice this is impossible in
Objective-C since everything can be invoked dynamically. But you can force a
compiler warning by not including the method in the @interface section.
Anyway, if you have a really, really private method, we usually prefix that using
a "_", eg:
- (void)_addHTTPCookie:(id)_cookie to:(NSMutableArray *)_a {
But attention: this *is* dangerous. Remember that the method is not really
privately scoped at runtime - so if a super- or subclass overrides the method,
you'll might get into trouble. So please name the method as specific as possible
- if the name describes exactly what it does, it is unlikely to break ;-)
BOOL
Objective-C also introduces a boolean type called 'BOOL' and the associated
values 'YES' and 'NO'. We think that BOOL arguments should not be treated
as strict booleans but rather in the common C sense that NO is 0 and
everything else is true.
Nevertheless we suggest to follow the rule to be tolerant on input and be
strict on output (return values!)
Some examples:
if (myBool) {} // correct
if (myBool == YES) {} // WRONG!!
if (myBool == NO) // OK
if (!myBool) // OK
but for output, try to be strict:
- (BOOL)isEqual:(id)_other {
return [_other compare:self] == 0 ? YES : NO;
}
The issue is a bit more controversial on other base types, like integer or
pointer values. Some examples what we consider OK:
if (myInt > 0) {} // correct
if (myInt) {} // WRONG!
if (myPtr) {} // OK
if (!myPtr) {} // so lala, not encouraged
if (myPtr == NULL) // OK
For most C types we just consider regular C conventions, eg anyone knows
what "if (ptr)" does, there is no need for "if (ptr != NULL)".
C level things
Some C related constructs.
if ((a = [self value]))
return YES;
Note: spaces after if, before and after =. Note that we do
not use braces for the return because it is just a single statement.
if (person == nil) {
...
}
We place the opening brace at the top, then ident with two spaces. Also note
that we do an explicit check for == nil instead of using just
!person which is valid C, but considered bad style (person is an
object, not a boolean).
Accessors
Accessor methods are methods which usually retrieve or set an ivar, they are mostly used in
conjunction with keyvalue coding. We always write the set-accessor first, followed by the
get-accessor. We do not leave an empty separator line between set and get accessor. Finally
we prefix the accessor section using an /* accessor */ comment.
/* accessors */
- (void)setBlurb:(NSString *)_value {
ASSIGNCOPY(self->blurb, _value);
}
- (NSString *)blurb {
return self->blurb;
}
- (void)setBlah:(NSDictionary *)_value {...
Note that we use "self->" to signal that blurb is an ivar. Further we use
ASSIGNCOPY, because _value is a basetype, so that we don't run into mutability
issues and gain speed.
.wod Files
Write more. but basics is, braces on the top line, indent by two spaces. Spaces
not after the element name, but after everything else:
TableView: SkyTableView {
list = persons;
}
If you have a set of really short declarations, you can line them up:
LoginLabel: WOString { value = labels.login; }
LogoutLabel: WOString { value = labels.logout; }
But please properly align stuff.