ios开发trick

译自:http://nshipster.com/new-years-2015/

成员函数的秘密

swift类和结构体的成员函数总是有以下2种等价调用形式:

[1, 2, 3, 4].reverse()
Array.reverse([1, 2, 3, 4])()

@()封装C字符串

除了封装数字和用在集合上,还可用于char *

NSString *propertyAttributesString =
@(property_getAttributes(class_getProperty([NSObject class], "description")));
//输出:T@"NSString",R,C

AmIBeingDebugged

判断app是否在调试器下运行的函数

#include <assert.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>

static bool AmIBeingDebugged(void) {
    int mib[4];
    struct kinfo_proc info;
    size_t size = sizeof(info);

    info.kp_proc.p_flag = 0;

    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();

    sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);

    return (info.kp_proc.p_flag & P_TRACED) != 0;
}

使用延迟变量

尽量避免声明optional变量,但没有初始值怎么办,使用lazy关键字,谨记在有实际值前不要调用getter,例如:

lazy var someModelStructure = ExpensiveClass()

如果在调用getter前调用了setter,则ExpensiveClass()永远不会执行。适用于viewDidLoad里才初始化的IBOutlet视图

获取插入到storyboard的容器视图的子控制器

// 1. A property has the same name as a segue identifier in XIB
@property (nonatomic) ChildViewController1 *childController1;
@property (nonatomic) ChildViewController2 *childController2;

// #pragma mark - UIViewController

- (void)prepareForSegue:(UIStoryboardSegue *)segue
                 sender:(id)sender
{
    [super prepareForSegue:segue sender:sender];

    // 2. All known destination controllers assigned to properties
    if ([self respondsToSelector:NSSelectorFromString(segue.identifier)]    ) {
        [self setValue:segue.destinationViewController forKey:segue.identifier];
    }
}

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // 3. Controllers already available bc viewDidLoad is called after prepareForSegue
    self.childController1.view.backgroundColor = [UIColor redColor];
    self.childController2.view.backgroundColor = [UIColor blueColor];
}

不编译重运行

Product > Perform Action > Run without Building

快捷键:⌘⌃R

快速获取Playground资源文件

Playground共享同一数据文件夹:/HOME/Documents/Shared Playground Data,以下为获取资源文件路径的方法:

import XCPlayground
func pathToFileInSharedSubfolder(file: String) -> String {
    return XCPSharedDataDirectoryPath + "/" + NSProcessInfo.processInfo().processName + "/" + file
}

processName即为Playground文件名,例如要获取本地json数据:

var jsonReadError:NSError?
let jsonData = NSFileManager.defaultManager().contentsAtPath(pathToFileInSharedSubfolder("data.json"))!
let jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &jsonReadError) as [AnyObject]

CocoaPods

查看app所使用的所有pods:

class-dump -C Pods_ /Applications/Squire.app | grep -o "Pods_\w+"

阻止dylib Hooking

在项目设置Other Linker Flags里添加:

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

获取app运行使用的语言

[[NSBundle mainBundle] preferredLocalizations]

NSDateFormatter +dateFormatFromTemplate:options:locale:

日期格式化模板例子:

NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];

NSString *dateFormat;
NSString *dateComponents = @"yMMMMd";

dateFormat = [NSDateFormatter dateFormatFromTemplate:dateComponents options:0 locale:usLocale];
NSLog(@"Date format for %@: %@",
    [usLocale displayNameForKey:NSLocaleIdentifier value:[usLocale localeIdentifier]], dateFormat);

dateFormat = [NSDateFormatter dateFormatFromTemplate:dateComponents options:0 locale:gbLocale];
NSLog(@"Date format for %@: %@",
    [gbLocale displayNameForKey:NSLocaleIdentifier value:   [gbLocale localeIdentifier]], dateFormat);

// Output:
// Date format for English (United States): MMMM d, y
// Date format for English (United Kingdom): d MMMM y

通过lldb获得内部常量

获得转场动画的持续时间:

  1. 设置断点:(lldb) br set -n "+[UITransitionView defaultDurationForTransition:]"
  2. 展示模态视图以触发断点,然后执行:(lldb) finish
  3. xmm0寄存器读取结果:(lldb) register read xmm0 --format float64,输出xmm0 = {0.4 0},0.4即为持续时间

自定义弱引用关联对象

@interface WeakObjectContainter : NSObject
@property (nonatomic, readonly, weak) id object;
@end

@implementation WeakObjectContainter

- (instancetype)initWithObject:(id)object {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.object = object;

    return self;
}
@end

使用:

objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainter alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC)

获取弱引用对象:

id object = [objc_getAssociatedObject(self, &MyKey) object];