心辰·Dev

NSHipster 博客干货整理 | Cocoa 框架

KVC 集合操作符

  • @count: (NSNumber)集合中对象总数
  • @sum: (NSNumber)集合中数值总和
  • @avg: (NSNumber)集合中平均数
  • @max: 最大值
  • @min: 最小值

    1
    2
    [products valueForKeyPath: @"@count"];
    [products valueForKeyPath: @"@sum.price"];
  • @unionOfObjects / @distinctUnionOfObjects: 返回 key path 对应的对象数组,不去重。@distinctUnionOfObjects去重。

    1
    [inventory valueForKeyPath: @"@unionOfObjects.name"];

KVO

  • 添加观察者:

    1
    - addObserver:forKeyPath:options:context:
  • context的建议赋值方式是static void *XXContext = &XXContext;keyPath的建议值是@selector(isFinished),添加反馈最佳实践如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context {
    if (context == XXContext) {
    if ([keyPath isEqualToString: NSStringFromSelector(@selector(isFinished))]) {
    // ...
    }
    }
    }

NSError

  • 错误码(code)一般定义为枚举中的一个常量。
  • 用户信息(userInfo)字典中常用的键有:
    • localizedDescription
    • localizedRecoverySuggestion
    • localizedFailureReason

NSError 的用法:

  • 消费者:

    1
    2
    3
    4
    5
    6
    7
    8
    NSError *error = nil;
    BOOL success = [[NSFileManager defaultManager]
    moveItemAtPath:@"/path/to/target"
    toPath:@"/path/to/destination"
    error:&error];
    if (!success) {
    NSLog(@"%@", error);
    }

    消除了 Objective-C 方法只有一个返回值的限制。

    1
    2
    3
    4
    5
    6
    7
    8
    [[session dataTaskWithRequest:request
    completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) {
    if (error) {
    NSLog(@"%@", error);
    } else {
    // ...
    }
    }] resume];
  • 生产者:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    - (BOOL)validateObject:(id)object
    error:(NSError * __autoreleasing *)outError {
    // call another API, passing &error into its (NSError **) parameter
    NSError *error = nil;
    BOOL success = ...;

    if (!success) {
    if (outError) {
    *outError = [NSError errorWithDomain:NSHipsterErrorDomain
    code:-42
    userInfo:@{NSUnderlyingErrorKey: error}];
    }
    }
    return success;
    }

NSOperation

为了让一个 NSOperation 操作开始执行,你可以直接调用-start,或者将它添加到操作队列 NSOperationQueue 中,添加之后,它会在队列排到它以后自动执行。

NSOperation 的状态机

分为三种状态:isReady —> isExecuting —> isFinished。

NSOperation 的优先级

  • NSOperationQueuePriorityVeryHigh
  • NSOperationQueuePriorityHigh
  • NSOperationQueuePriorityNormal
  • NSOperationQueuePriorityLow
  • NSOperationQueuePriorityVeryLow

构建依赖关系

1
2
3
[resizingOperation addDependency:networkingOperation];
[operationQueue addOperation:networkingOperation];
[operationQueue addOperation:resizingOperation];

只有networkingOperation状态isFinishedYES, resizingOperation操作才会开始。

NSSortDescriptor

  • key: 对于一个给定的集合,要对每个集合对象进行排序的值的键。
  • ascending: YES ascending 表示升序。
  • localizedStandardCompare: 根据当前语言环境的规则进行排序。

NSExpression

算数表达式

1
2
NSExpression *expression = [NSExpression expressionWithFormat:@"4 + 5 - 2**3"];
id value = [expression expressionValueWithObject:nil context:nil]; // => 1

利用函数

1
2
3
NSArray *numbers = @[@1, @2, @3, @4, @4, @5, @9, @11];
NSExpression *expression = [NSExpression expressionForFunction:@"stddev:" arguments:@[[NSExpression expressionForConstantValue:numbers]]];
id value = [expression expressionValueWithObject:nil context:nil]; // => 3.21859...

支持自定义函数。

统计数字

  • average:
  • sum:
  • count:
  • min:
  • max:
  • median:
  • mode:
  • stddev:

基本运算

  • add:to:
  • from:subtract:
  • multiply:by:
  • divide:by:
  • modulus:by:
  • abs:

高级运算

  • sqrt:
  • log:
  • ln:
  • raise:toPower:
  • exp:

边界函数

  • ceiling: - (不小于数组中的值的最小整数值)
  • trunc: - (最接近但不大于数组中的值的整数值)

NSPredicate

在不可变集合中应用过滤方法,如filteredArrayUsingPredicate:,那么会返回一个经过过滤的不可变集合。可变集合则直接移除不符合规则的对象。这一过程在内存中进行。
Core Data中也能使用NSPredicate, 只是评估过程由持久化存储助理(persistent store coordinator)在管理对象上下文中(managed object context)进行。

NSCompoundPredicate

NSCompoundPredicate用于合成现成的NSPredicate

1
[NSCompoundPredicate andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"age > 25"], [NSPredicate predicateWithFormat:@"firstName = %@", @"Quentin"]]];

1
[NSPredicate predicateWithFormat:@"(age > 25) AND (firstName = %@)", @"Quentin"];

以上两段代码功能相同。

Block Predicates

用Block封装任意的运算。

1
2
3
4
5
NSPredicate *shortNamePredicate = [NSPredicate predicateWithBlock: ^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [[evaluatedObject firstName] length] <= 5;
}];

NSLog(@"Short Names: %@", [people filteredArrayUsingPredicate: shortNamePredicate]);

NSFileManager

通过NSFileManagerattributesOfItemAtPath:error:可以访问很多文件的属性,如下的代码得到文件的创建日期:

1
2
3
4
if ([fileManager fileExistsAtPath:filePath]) {
NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
creationDate = attributes[NSFileCreationDate];
}

如果用一个代理接收关于文件移动、复制、删除等操作的通知,应该创建一个NSFileManager实例,将代理对象指向 fileManager 的代理。

NSValue

  • valueWithBytes:objCType:
    创建并返回一个包含指定 Objective-C 类型数值的 NSValue 对象。

  • valueWithNonretainedObject:
    创建并返回一个包含 Objective-C 对象的 NSValue 对象。这样允许一个对象,在不遵从 <NSCopying> 协议的情况下,添加到集合中。

NSValueTransformer

使用 Matt 大神提供的三方库 TransformerKit, 用以下代码注册一个转换字符串首字母大写的NSValueTransformer

1
2
3
4
5
6
7
NSString * const TKCapitalizedStringTransformerName = @"TKCapitalizedStringTransformerName";

[NSValueTransformer registerValueTransformerWithName:TKCapitalizedStringTransformerName
transformedValueClass:[NSString class]
returningTransformedValueWithBlock:^id(id value) {
return [value capitalizedString];
}];

NS_ENUM & NS_OPTIONS

简单定义NS_ENUM宏如下:

1
2
3
#ifndef NS_ENUM
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#endif

enum 还可以用来定义位掩码。数字二进制的每一位可以看做一个布尔值,用|&进行组合运算。和 NS_ENUM 的语法相同,使用 NS_OPTIONS 宏定义位掩码。

NSCache

  • NSCache 实际上是可以自动清理内存空间的 NSMutableDictionary, 而且它的 keys 不用遵从 <NSCopying>
  • 由于cost参数定义不明,尽量不要使用setObject:forKey:cost:

NSHashTable & NSMapTable

可以存储弱引用的值,或使用一个没有遵守<NSCopying>的对象作为键。
NSHashTable对应NSSetNSMapTable对应NSDictionary
使NSMapTable支持对象下标索引,添加以下的 Category 即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation NSMapTable (NSHipsterSubscripting)

- (id)objectForKeyedSubscript:(id)key {

return [self objectForKey:key];
}

- (void)setObject:(id)obj forKeyedSubscript:(id)key {
if (obj != nil) {
[self setObject:obj forKey:key];
} else {
[self removeObjectForKey:key];
}
}

@end