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.
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.
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
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).
So many things to write down, so little time, ...
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.
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.
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:
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 ;-)
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
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.
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.
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;
}
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.
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.
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 ...)
}
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 */
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 ;-)
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)".
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).
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.
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.