ObjectiveC笔记

block

  • block分stack/heap/global:block通常是其创建时所在作用域的栈变量,出了作用域这块内存可能被释放可能不释放;通过copy后block变成堆变量,使用引用计数;未引用任何外部变量和参数的block是全局变量,copy无用也不dealloc。

  • block的参数列表为空时是接受_可变参数_,void (^block)();void (^block)(void);不同。

    参见:ObjC非主流代码技巧

  • block中避免循环引用self:先在block外:

    1
    __weak __typeof(self) weakSelf = self;
    然后在block中:

    1
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    注意:block中的_ivar会隐式引用self(即self->_ivar),要改成weakSelf->_ivar或weakSelf.ivar。更优雅的方法是使用ReactiveCocoa的libextobjc中的@weakify(self)@strongify(self)

  • 在block中要小心使用NSAssert,因为NSAssert宏展开中使用了self很可能导致循环引用,可用NSCassert

selector

  • performSelector延迟调用可用dispatch_after()替代,如:

    1
    2
    3
    4
    5
    // [self performSelector:@selector(doSomething) withObject:nil afterDelay:5.0];
    dispatch_time time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^(void){
    [self doSomething];
    });

  • performSelector调用的方法至多接收两个id参数,要调用其他签名形式的方法可使用NSInvocation实现。比如,为调用类似- (void)drawPoint:(CGPoint)point;形式的方法,可写工具类:

    1
    2
    3
    4
    5
    6
    7
    8
    + (void)performSelector:(SEL)selector atTarget:(id)target withArgument:(void *)argument {
    NSMethodSignature *signature = [target methodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = target;
    invocation.selector = selector;
    [invocation setArgument:argument atIndex:2];
    [invocation invoke];
    }
    参见:NSInvocation更详细用法

  • 当object收到未知消息时会调用- (void)forwardInvocation:(NSInvocation *)anInvocation,因此有机会转发消息。

    参见:Message Forwarding

预处理指令

  • 为确保文件被引用时开启ARC,添加

    1
    2
    3
    #if ! __has_feature(objc_arc)
    #error This class is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
    #endif

  • #pragmasupress warning:

    1
    2
    3
    4
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wxxx"
    // ...
    #pragma clang diagnostic pop

    • category中方法重载的warning:-Wobjc-protocol-method-implementation

    • 调用deprecated方法的warning:-Wdeprecated-declarations

    pragma的其他用法见Pragmas Aren’t Just For Marks

其他

  • 在category中使用Method Swizzle,要在+load()中swizzle,在+initialize()中调用会因category的initialize()和原类中被重载的initialize()的两次调用互相抵消。

  • 混用ObjectiveC和C++

  • NSMutableArray的底层实现__NSArrayM是个循环数组,所以在首尾两端插入删除无需移动元素,最坏情况在中间插入删除要移动n/2个元素。插入元素时若空间不足,则空间按1.625倍增长,空间增长后即使删除元素空间也不再变小。见Exposing NSMutableArray

  • 给类添加类似array的index存取功能,实现

    1
    2
    - (id)objectAtIndexedSubscript:(NSUInteger)idx;
    - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;

    添加类似dict的key存取功能,实现

    1
    2
    - (id)objectAtKeyedSubscript:(id <NSCopying>)key;
    - (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;

  • release build中去掉NSLog,在Prefix.pch中添加:

    1
    2
    3
    #ifdef NDEBUG
    #define NSLog(...) /* suppress NSLog for release mode */
    #endif

  • non-retaining array/set for delegates:

    1
    2
    3
    4
    CFArrayCallBacks callbacks = { 0, NULL, NULL, CFCopyDescription, CFEqual };
    NSMutableArray *noRetainArray = (NSMutableArray *)CFArrayCreateMutable(NULL, 0, &callbacks);

    NSMutableSet *noRetainSet = (NSMutableSet *)CFSetCreateMutable(NULL, 0, NULL);

  • 倒序数组:

    1
    array.reverseObjectEnumerator.allObjects`

  • 快速遍历含不同类型x值的集合:

    1
    2
    id<NSFastEnumeration> collection = values;
    for (id object in collection) { … }

  • 作为dict键值的object要遵循<NSCopying>,如果不遵循又要做键值,使用

    1
    [NSValue valueWithNonretainedObject:object]]

  • 使用block遍历collection:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Array
    NSArray *array = /* ... */;
    [array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
    }];
    // Dictioinary
    NSDictionary *dict = /* ... */;
    [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
    }];
    // Set
    NSSet *set = /* ... */;
    [set enumerateObjectsUsingBlock:^(id object, BOOL *stop) {
    }];

    此外还有带options的版本,如:enumerateObjectsWithOptions:usingBlock:

    通过options可指定是否并行迭代NSEnumerationConcurrent,是否反向迭代NSEnumerationReverse

  • 使用NSURL.h、NSPathUtilities.h、NSURLComponents操作url

  • 使用NSByteCountFormatter美化下载字节计数:[formatter stringFromByteCount:byteCount]

  • 只在某.m中使用的常量:

    1
    2
    // .m
    static const NSString *kStringConstant = @"VALUE";`
    全局常量:

    1
    2
    3
    4
    // .h
    extern NSString *const EOCStringConstant;
    // .m
    NSString *const EOCStringConstant = @"VALUE";

  • 使用宏NS_ENUMNS_OPTIONS来定义指定类型的枚举

  • 使用NSCache而不是NSDictionary来cache,可在系统内存不足时自动清理

  • switch语句处理枚举值时不要实现default语句,这样当添加新的枚举值后,编译器会警告你没有处理完所有枚举值