Small information nuggets and recipies about Objective-C
✂️ Objective-C
Table of Contents
- Don’t refer to your own class by name
- Method -description is normally used as an internal text representation
- Enable test feature from a command line argument
- Default values with GNU-style ternary operator
- Manipulating collections using blocks
- Declaring instance variables (ivars)
- Assertions & Release builds
- Naming private & addition methods to avoid naming conflicts
- Check if running under unit tests
- Annotate intentional fall-through between switch labels
- Using meta information in NSLog
- Return values from code blocks
- Define enums and bitmasks
- Boxed enums: converting a standard enum to an integer type, and boxing the value accordingly.
- Iterate through an enum…
- Random number generator
- Constants declaration
- Singletons with a thread-safe pattern
- Custom warning and error directives
- Suppress specific warnings in code
- Temporarily silence unused variables warning
- Xcode custom build phases
(most recent on top)
Don’t refer to your own class by name
- … because you don’t know what the concrete class will be
- How to Botch Your Objective-C Factory Method - Quality Coding
self // From a class method
[self class] // From an instance method
Method -description
is normally used as an internal text representation
- i.e. for debugging
- Overriding NSObject’s description | Dr John Wordsworth
- Auto Description Category for NSObject (any object) | At Kit
-(NSString *)description
{
return [NSString stringWithFormat:@"<GameObject: %@, Position: %f, %f>",
[self objectID], [self position].x, [self position].y];
}
Enable test feature from a command line argument
… this is useful for debugging development builds with the command line argument
-TestFeatureEnabled YES
[[NSUserDefaults standardUserDefaults] boolForKey:@"TestFeatureEnabled"]
Default values with GNU-style ternary operator
NSLog(@"%@", @"a" ?: @"b"); // => @"a"
NSLog(@"%@", nil ?: @"b"); // => @"b"
Manipulating collections using blocks
- BlocksKit/BlocksKit/Core/NSArray+BlocksKit.m at master · BlocksKit/BlocksKit
- [BlocksKit][1] is a framework for OS X Lion and newer and a static library for iOS 5 and newer. | LinkedIn
Declaring instance variables (ivars)
@interface ClassName : NSObject {
int ivar1; // @protected by default; could use @private (legacy) or @public (bad practice)
}
@end
@implementation ClassName () {
int ivar2; // @private by default; can change to @protected
}
@end
@implementation ClassName {
int ivar3; // @private by definition; other visibility keywords are ignored
}
@end
Assertions & Release builds
- NSAssertionHandler : NSHipster
- Expanded use of Asserts - Krzysztof Zabłocki
- The Art of Assertion (as it pertains to Xcode) | Learn & Master Cocos2D Game Development
NSAssert()
is disabled if the preprocessor macro
NS_BLOCK_ASSERTIONS
is defined for Release builds (defaults change per Xcode version)
Naming private & addition methods to avoid naming conflicts
- Coding Guidelines for Cocoa: Naming Methods
- Programming with Objective-C: Customizing Existing Classes
- (void)pfx_myMethodName;
Check if running under unit tests
BOOL runningTests = NSClassFromString(@"XCTest") != nil;
Annotate intentional fall-through between switch labels
- aka “follow-through”
- Clang Language Extensions — Clang 3.4 documentation
- LLVM Project Blog: Clang Warnings
-Wimplicit-fallthrough
— Enabling this warning will cause a diagnostic to be emitted for all fallthroughs in switch statements that have not been annotated. Requires compilation in C++11 mode. Clang-specific warning, available in the 3.2 release.
[[clang::fallthrough]]
Using meta information in NSLog
- objective c - How to print out the method name and line number and conditionally disable NSLog? - Stack Overflow
- Technical Q&A QA1669: Improved logging in Objective-C
NSLog(@"%d", ...)
__LINE__ // Current line number in the source code file
NSLog(@"%s", ...)
__func__ // Current function signature
__PRETTY_FUNCTION__ // Like __func__, but includes verbose type information in C++ code
__FILE__ // Full path to the source code file
NSLog(@"%@", ...)
NSStringFromSelector(_cmd) // Name of the current selector
NSStringFromClass([self class]) // Name of the current object's class
[NSThread callStackSymbols] // NSArray of the current stack trace as programmer-readable strings
[[NSString stringWithUTF8String:__FILE__] lastPathComponent] // Name of the source code file
Return values from code blocks
self.bounds = ({
CGRect bounds = self.bounds;
bounds.size.height = self.currentYPosition + SHEETINSETY;
bounds;
});
const BOOL anyValueIsXyz = (^{
for (QwertyValue *value in values) {
if (value.isXyz) {
return YES;
}
}
return NO;
}());
Define enums and bitmasks
typedef NS_ENUM(NSInteger, PFXEnumName) {
PFXEnumNameValueA,
PFXEnumNameValueB
};
typedef NS_OPTIONS(NSUInteger, PFXEnumName) {
PFXEnumNameValueA = 0,
PFXEnumNameValueB = 1 << 0,
PFXEnumNameValueC = 1 << 1,
PFXEnumNameValueD = 1 << 2
};
… usage
PFXEnumNameValueA | PFXEnumNameValueC
Boxed enums: converting a standard enum to an integer type, and boxing the value accordingly.
@(SomeStandardEnumValue) // boxing
[boxedNum nativeTypeValue] // unboxing
Iterate through an enum…
… fragile pattern; assumes too much: incremental values only, no gaps numeric order, count must be the last element, etc. ⚠️
typedef NS_ENUM(NSInteger, PFXEnumName) {
PFXEnumNameValueA,
PFXEnumNameValueB,
PFX_ENUM_NAME_COUNT
};
for (int i = 0; i < PFX_ENUM_NAME_COUNT; i++) {
NSLog(@"%@", (i == PFXEnumNameValueA)? @"got the element" : @"nope, not yet");
}
Random number generator
// random int between [0..upper_bound[
arc4random_uniform(upper_bound);
Constants declaration
- C Storage Classes : NSHipster
- Objective-C static, extern, public variables - Stack Overflow
- Difference between external variable of some sort and static variable - Stack Overflow
… within the same translation unit (i.e. current source being compiled + all the included headers)
// file.m
static NSString * const PFXExampleConstantA = @"value";
static const int PFXExampleConstantB = 1;
… globally available to all
// file.h
extern NSString * const PFXExampleConstantA;
// file.m
NSString * const PFXExampleConstantA = @"value";
// file.h
extern const struct PFXExampleConstantsStruct
{
NSString *constantA;
int constantB;
struct
{
NSString *constantC;
NSString *constantD;
} scope;
} PFXExampleConstants;
// file.m
const struct PFXExampleConstantsStruct PFXExampleConstants = {
.constantA = @"value",
.constantB = 1,
.scope = {
.constantC = @"valueC",
.constantD = @"valueD"
}
};
// usage
(void)PFXExampleConstants.scope.constantC
Singletons with a thread-safe pattern
- Cocoa Samurai: Singletons: You’re doing them wrong
- Fundamental iOS design patterns: SharedInstance (Singleton in Objective C) | DaveOnCode
- Singletons in Objective-C - Matt Galloway
… using Grand Central Dispatch
+ (instancetype)shared<#ClassName#> {
static id sharedInstance = nil;
static dispatch_once_t token;
dispatch_once(&token, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Custom warning and error directives
#warning pay attention to this
#error continue here…
Suppress specific warnings in code
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wwarning-type"
#pragma clang diagnostic ignored "-Wanother-warning"
// …
#pragma clang diagnostic pop
Temporarily silence unused variables warning
__unused int someVar;
(void)someVar;
#pragma unused (someVar, anotherVar)
Xcode custom build phases
… TODO & FIXME comments as warnings
KEYWORDS="TODO:|FIXME:|\?\?\?:|\!\!\!:"
find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($KEYWORDS).*\$" | perl -p -e "s/($KEYWORDS)/ warning: \$1/"
… show test coverage report
open -g -a Xcoverage.app "$OBJECT_FILE_DIR-normal/$CURRENT_ARCH" >/dev/null 2>&1 || true