Swift 诞生, 意味着 Objective-C 语言终究会推出历史舞台.

今天打开邮件发现, 收到 NSHipster 社区对The NSHipster Fake Book 这本书的更新邮件, 整本书没多少页, 主要记录了些在开发过程中常用的代码片段, 用一点时间记录了些感觉不错的 Tricks, 方便今后的查阅.

1. Language & Runtime

# 1.1 Creating String Representations for Enumerated Type

NSString * const UITableViewCellStyleDescription[] = {
	[UITableViewCellStyleDefault] = @"Default",
	[UITableViewCellStyleSubtitle] = @"Subtitle",
	[UITableViewCellStyleValue1] = @"Value 1",
	[UITableViewCellStyleValue2] = @"Value 2"
};
UITableViewCellStyle style = ...;
NSString *description = UITableViewCellStyleDescription[style];

# 1.2 Adding a Property to a Category

//NSObject+AssociatedObject.h
@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject; @end
//NSObject+AssociatedObject.m
@implementation NSObject (AssociatedObject)
@dynamic associatedObject;
- (void)setAssociatedObject:(id)object {
	objc_setAssociatedObject(self, @selector(associatedObject), 	object,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
	return objc_getAssociatedObject(self, @selector(associatedObject));
}

# 1.3 Swizzling a Method

//Swizzling a Method
#import <objc/runtime.h>
@implementation UIViewController (Tracking)

+ (void)load {
	static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
		Class class = [self class];
		// When swizzling a class method, use the following:
		// Class class = object_getClass((id)self);
		SEL originalSelector = @selector(viewWillAppear:);
		SEL swizzledSelector = @selector(xxx_viewWillAppear:);
		Method originalMethod = class_getInstanceMethod(class, originalSelector);
		Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
		BOOL didAddMethod = class_addMethod(class,
						    originalSelector, 	method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
		if (didAddMethod) {
		    class_replaceMethod(class,
					swizzledSelector, method_getImplementation(originalMethod), 	method_getTypeEncoding(originalMethod));
		}
		else {
		    method_exchangeImplementations(originalMethod, swizzledMethod);
		}
	});
}

#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
	[self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", self);
}

# 1.4 Determining the Type of a Property

//Determining the Type of a Property
const char *attributes = property_getAttributes(class_getProperty([self  class], sel_getName(@selector(property))));
NSString *typeAttribute = [[[NSString stringWithUTF8String:attributes]  componentsSeparatedByString:@","] firstObject];
const char *propertyType = [[typeAttribute substringFromIndex:1] UTF8String];
if (strcmp(propertyType, @encode(float)) == 0) {
	// float
} else if (strcmp(propertyType, @encode(int)) == 0) {
	// int
}

# 1.5 Specifying the Availability of a Method

//Specifying the Availability of a Method
void foo() __attribute__((availability(macosx, introduced=10.4,
					   deprecated=10.6,
					   obsoleted=10.7)));

# 1.6 Hiding a Class

//Hiding a Class
__attribute__((visibility("hidden"))
@interface HiddenClass : Superclass // ...
@end

# 1.7 Hiding a Method

//Hiding a Method
- (BOOL)respondsToSelector:(SEL)selector {
    if (selector == @selector(methodToHide)) {
	return NO; }
    return [[self class] instancesRespondToSelector:selector];
}

# 1.8 Ignoring Compiler Warnings

//Ignoring Compiler Warnings
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu" id object = object ?: [NSNull null];
#pragma clang diagnostic pop

# 1.9 Determining the Current System Memory Usage

//Determining the Current System Memory Usage
#import <mach/mach.h>
#import <sys/sysctl.h>
vm_statistics_data_t vmStats;
mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;
kern_return_t status = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmStats, &infoCount);
natural_t memoryUsage = 0; // in bytes
if (status == KERN_SUCCESS) {
    memoryUsage = vmStats.wire_count * 1024.0;
}

# 1.10 Creating Variadic Method

//Creating Variadic Method
- (void)method:(id)object, ... NS_REQUIRES_NIL_TERMINATION { va_list args;
    va_start(args, object);
    while (object) {
	// ...
	object = va_arg(args, id);
    }
    va_end(args);
}

# 1.11 Creating a Variadic Function

//Creating a Variadic Function
static double average(int count, ...) {
    va_list args;
    va_start(args, count);
    int sum = 0;
    for (int i = 0; i < count; i++) {
	sum += va_arg(args, int);
    }
    va_end(args);
    return (double)sum / (double)count;
}
double avg = average(4, 2, 3, 4, 5);

# 1.12 Overloading Functions

//Overloading Functions
#include <math.h>
__attribute__((overloadable)) CGFloat CGFloat_floor(double d) {
    return (CGFloat)floor(d);
}
__attribute__((overloadable)) CGFloat CGFloat_floor(float f) {
    return (CGFloat)floorf(f);
}

float __attribute__((overloadable)) tgsin(float x) {
    return sinf(x);
}
double __attribute__((overloadable)) tgsin(double x) {
    return sin(x);
}
long double __attribute__((overloadable)) tgsin(long double x) {
    return sinl(x);
}

# 1.13 Conditionally Compiling for iOS & OS X Targets

//Conditionally Compiling for iOS & OS X Targets
#import <Availability.h>
id color = nil;
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
color = [NSColor orangeColor];
#elsif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
color = [UIColor purpleColor];
#endif

# 1.14 Requiring Method to call super

//Requiring Method to call super
- (void)method __attribute__((objc_requires_super));

# 1.15 Determining the Caller of a Method

Stack Framework Address Class Function Line
1 UIKit 0x004f UIKit Function 32
//Determining the Caller of a Method
NSString *callerSymbol = [NSThread callStackSymbols][1];
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@" +,-.?[]"];
NSArray *components = [callerSymbol componentsSeparatedByCharactersInSet:characterSet];
components = [components filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self <> ’’"]];
NSString *stack = components[0];
NSString *framework = components[1];
NSString *address = components[2];
NSString *classCaller = components[3];
NSString *functionCaller = components[4];
NSString *lineCaller = components[5];

# 1.16 Intentionally Crashing the Current Process

//Intentionally Crashing the Current Process
__builtin_trap();

2. Grand Central Dispatch

# 2.1 Benchmarking the Execution Time of an operation

//Benchmarking the Execution Time of an operation
extern uint64_t dispatch_benchmark(size_t count, void (^block)(void))...----
size_t const objectCount = 1000;
uint64_t t = dispatch_benchmark(10000, ^{
    @autoreleasepool { id obj = @42;
	    NSMutableArray *array = [NSMutableArray array]; for (size_t i = 0; i < objectCount; ++i) {
		[array addObject:obj];
	    }
	}
});
NSLog(@"-[NSMutableArray addObject:] : %llu ns", t);

# 2.2 Dispatching a Timer

//Dispatching a Timer
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
int64_t delay = 30 * NSEC_PER_SEC;
int64_t leeway = 5 * NSEC_PER_SEC;
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, delay , leeway);
dispatch_source_set_event_handler(timer, ^{
    NSLog(@"Ding Dong!");
});
dispatch_resume(timer);

# 2.3 Monitoring Local File Changes

//Monitoring Local File Changes
NSURL *fileURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
       inDomains:NSUserDomainMask] firstObject];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
int fileDescriptor = open([fileURL fileSystemRepresentation], O_EVTONLY);
unsigned long mask = DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_DELETE;
__block dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileDescriptor,mask,queue);
dispatch_source_set_event_handler(source, ^{
dispatch_source_vnode_flags_t flags = dispatch_source_get_data(source);
if (flags) {
    dispatch_source_cancel(source);
    dispatch_async(dispatch_get_main_queue(), ^{
    // ...
       });
    }
});
dispatch_source_set_cancel_handler(source, ^{
    close(fileDescriptor);
});
dispatch_resume(source);

3. Random

# 3.1 Creating a Random Integer

//Creating a Random Integer
// Random int between 0 and N - 1
NSUInteger r = arc4random_uniform(N);
// Random int between 1 and N
NSUInteger r = arc4random_uniform(N) + 1;

# 3.2 Creating a Random Double

//Creating a Random Double
srand48(time(0));
double r = drand48();

// Random Color
srand48(time(0));
UIColor *color = [UIColor colorWithRed:drand48()
				   green:drand48()
				    blue:drand48()
				  alpha:1.0f];

# 3.3 Creating a Random String

//Creating a Random String
NSString *letter = [NSString stringWithFormat:@"%c", arc4random_uniform(26) + ’a’];

# 3.4 Creating a Random Date

//Creating a Random Date
NSTimeInterval timeInterval = (NSTimeInterval)arc4random_uniform(pow(2.0, 32.0) - 1.0);
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:timeInterval];

4. Collections

# 4.1 Accessing Mutable Dictionary in a Thread-Safe Manner

//Accessing Mutable Dictionary in a Thread-Safe Manner
@property NSMutableDictionary *mutableDictionary;
@property dispatch_queue_t queue;
- (void)setObject:(id)object
	   forKey:(id)key{
     dispatch_barrier_async(self.queue, ^{
	self.mutableDictionary[key] = object;
    });
}

# 4.2 Reversing an Array

//Reversing an Array
NSArray *array = ...;
NSArray *reversed = [[array reverseObjectEnumerator] allObjects];

# 4.3 Filtering Objects in Array by Class

//Filtering Objects in Array by Class
NSArray *mixedArray = @[@"a", @"b", @"c", @(1), @(2), @(3)];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [NSString class]];
NSArray *letters = [mixedArray filteredArrayUsingPredicate:predicate];

# 4.4 Computing the Sum of an Array

//Computing the Sum of an Array
NSArray *array = @[@1, @2, @3];
NSNumber *sum = [array valueForKeyPath:@"@sum.self"];

# 4.5 Removing Duplicate Objects from an Array

//Using KVC Collection Operator
NSArray *array = @[@"a", @"b", @"c", @"a", @"d"];
NSArray *uniqueArray = [array valueForKeyPath:@"@distinctUnionOfObjects.self"];
//Using NSSet
NSSet *set = [NSSet setWithArray:array];
NSArray *uniqueArray = [set allObjects];
//Using NSOrderedSet
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:array];
NSArray *uniqueArray = [orderedSet array];

5. Cartography

# 5.1 Converting Degrees to Radians

//Converting Degrees to Radians
double radians = degrees * M_PI / 180.0f;

# 5.2 Converting Radians to Degrees

//Converting Radians to Degrees
double degrees = radians * 180.0f / M_PI;

# 5.3 Convert Radians to CLLocationDirection

CLLocationDirection direction = fmod(degrees, 360.0f) + 90.0f;

6. Graphics

# 6.1 Animating a CAGradientLayer

//Animating a CAGradientLayer
NSArray *colors = @[(id)[[UIColor redColor] CGColor], (id)[[UIColor orangeColor] CGColor]];
NSArray *locations = @[@(0.0), @(1.0)]; NSTimeInterval duration = 1.0f;
[UIView animateWithDuration:duration animations:^{
    [CATransaction begin];
    {
	[CATransaction setAnimationDuration:duration];
	[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
	[(CAGradientLayer *)self.layer setColors:colors];
	[(CAGradientLayer *)self.layer setLocations:locations];
    }
    [CATransaction commit];
}];

+ (Class)layerClass {
    return [CAGradientLayer class];
}

#pragma mark - CALayerDelegate
- (id <CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event{

    id <CAAction> action = [super actionForLayer:layer forKey:event];
    if ((!action || [(id)action isEqual:[NSNull null]]) && [event isEqualToString:@"colors"]) {
	action = [CABasicAnimation animationWithKeyPath:event];
    }
    return action;

}

# 6.2 Creating an Image for a Swatch of Color

//Creating an Image for a Swatch of Color
static UIImage * UIImageForSwatchOfColorWithSize(UIColor *color, CGSize size)
{
    UIImage *image = nil;
    CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
    UIGraphicsBeginImageContext(rect.size); {
	CGContextRef c = UIGraphicsGetCurrentContext();
	CGContextSetFillColorWithColor(c, [color CGColor]); CGContextFillRect(c, rect);
	image = UIGraphicsGetImageFromCurrentImageContext(); }
    UIGraphicsEndImageContext();
    return image;
}

# 6.3 Getting Color RGB Components

//Getting Color RGB Components
UIColor *color = ...;
CGFloat r, g, b, a;
[color getRed:&r green:&g blue:&b alpha:&a];

7. UIKit

# 7.1 Creating a Snapshot of a View

//Creating a Snapshot of a View
UIView *view = ...;
double scale = [[UIScreen mainScreen] scale];
UIImage *snapshot = nil;
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, scale); {
    if ([self respondsToSelector:@selector(drawViewHierarchyInRect: afterScreenUpdates:)]) {
	  [self drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
    } else {
	  [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    }
    snapshot = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();

# 7.2 Determining if UIViewController is Visible

//Determining if UIViewController is Visible
- (BOOL)isVisible {
	return [self isViewLoaded] && self.view.window;
}

# 7.3 Removing Drop Shadow from UIWebView

//Removing Drop Shadow from UIWebView
for (UIView *view in [[[webView subviews] objectAtIndex:0] subviews]) {
    if ([view isKindOfClass:[UIImageView class]] && view.frame.size.width == 1.0f) {
	      view.hidden = YES;
	}
}

# 7.4 Making a Device Vibrate

//Making a Device Vibrate
@import AudioToolbox;
// Plays an alert noise if vibration not supported on device
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);

// No-op on devices that do not support vibration
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);