• Core Data Programming Guide
    • 数据库的table对应object,table的column对应property
    • NSManagedObjectContext通过coordinator管理存储,NSPersistentStoreCoordinator将实际的NSPersistentStore(如sqlite文件)和NSManagedObjectModel(编译后的data model)关联。
  • CoreData使用多个数据库:
    1. 在Xcode的DataModel中添加config,让entiey分属不同的config
    2. ManagedObjectContext只关联一个PersistentStoreCoordinator,coordinator中可添加多个PersistentStore,让这些store分别对应不同的config:[coordinator addPersistentStoreWithType:configuration:URL:options:error:]
    3. relationship只能用在同个PersistentStore中,跨store要使用FetchedRequest
    参考:stackoverflow

加密CoreData

  • 使用sqlcipher加密sqlite,可使用其Xcode5分支

    1. 编译:

       ./configure --with-crypto-lib=commoncrypto --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2 -DSQLCIPHER_CRYPTO_CC" LDFLAGS="-framework Security" && make sqlite3.c

      要禁掉sqlcipher项目的warnings,在Build Phases的Compile Sources中给sqlite3.c加上Compiler Flags:-w

    2. sqlcipher.xcodeproj拉进Xcode作为子项目,在父项目Build Phases的Target DepedenciesLink Binary With Libraries中添加sqlcipher

    3. 在父项目Build Settings的Other C Flags中添加:-DSQLITE_HAS_CODEC

  • 使用encrypted-core-data加密CoreData,encrypted-core-data依赖sqlcipher

  • 使用MagicalRecord方便CoreData,见使用入门

    • 使用前需要在.pch文件中添加:

        #define MR_SHORTHAND
        #import "CoreData+MagicalRecord.h"

  • 所有变量都是传引用,没有传值的

  • 数字的内部表示都为double,所以1和1.0是相同的值

  • 被当做false的值:null, undefined, ’’, 0, NaN

  • null,undefined不是对象;布尔,数字,字符串类似对象;其他都是对象,即可变的键值对集合

  • typeof检测的类型:‘boolean’, ‘number’, ‘string’, ‘undefined’, ’function‘, ‘object’

  • key:value对中的key是合法标识符时(仅含数字字母下划线)可以不加引号

  • 引用对象属性时可以用.[],若属性名不是合法标识符则只能用[]

  • hasOwnProperty()查对象属性时不查prototype链,delete可删除对象属性

  • 只有函数会创建作用域,花括号代码块不创建作用域

  • ||可提供默认值:var middle = stooge["middle-name"] || "(none)"

  • 除了声明时定义的形式参数,函数还接受两个附加的参数:thisargumentsthis值取于四种模式之一:

    1. 当函数作为对象的属性时,this指向该对象
    2. 当函数不是对象的属性时,this指向全局对象。这设计错误导致内部函数的this指向了全局对象,而不是外部函数的this。解决方法是在外部函数中定义var that = this;,然后在内部函数中通过that来访问。
    3. new Func()创建新对象时,this指向该对象
    4. func.apply(that, arguments)调用函数时,this指向传进的that

    arguments可访问函数被调用时欲传进的实参列表,包括那些没有形参对应的多余参数。arguments类似数组,有length属性可下标访问,但设计错误使它没成为数组。吧arguments变成数组,使用var args = Array.prototype.slice.apply(arguments)

  • 函数总会返回一个值,没有指定返回值时返回undefined。如果以new Func()形式调用且返回值不是对象,则返回this(该新对象)。

  • 模块:一个定义了私有变量和函数的函数,利用闭包创建可访问私有变量和函数的接口函数,最后返回这个接口函数,或者直接把接口内容存进某个可访问的函数中。

  • 基于prototype继承是基于对象的继承,而不是基于类的继承

  • 继承最好使用函数化模式:

      var constructor = function(kv) {
          私有实例变量(可通过闭包访问)
          var that = 要继承的一个新对象
          给that添加属性或方法
          return that
      }
  • 数组只是类似数组的对象,它把数组下标转成字符串,用其作为属性。可存放任意类型元素,内部表示不是线性分配的内存。

  • 数组的length等于其最大整数属性名加1,不一定等于数组的元素个数

References

  1. Javascript秘密花园
  2. 《Javascript语言精粹》

Promise

  • caller调then(callback),callee调resolve(),两者都被调用后callback才会执行
  • 多个then()链式调用时,上一个then()的返回值是下一个then()的输入参数
  • then(callback, errback)的errback可提供reject()后的错误处理

References

  1. JavaScript Promises … In Wicked Detail

  • UIView与display相关的功能都在CALayer中,UIView仅给CALayer添上touch处理功能。 例如,UIView的位置相关信息保存在view.layer的四个属性中,view.frame根据它们导出:

    • view.layer.position == view.center
    • view.layer.bounds == view.bounds
    • view.layer.anchorPoint
    • view.layer.transform
  • view.layer.anchorPoint默认为(0.5,0.5),要将其设为view中另一点point:

    1
    2
    3
    CGSize viewSize = view.frame.size;
    view.layer.anchorPoint = CGPointMake(point.x/viewSize.width, point.y/viewSize.height);
    view.layer.position = point;

  • view.bounds指定view自己的坐标系统内正被观察到的矩形区域。比如,scrollView在scrolling时其实只是改变scrollView.bounds.origin (即scrollView.contentOffset),而scrollView.contentSize只是和scrollView.bound.size一起限制scrollView.bounds.origin的变动范围。参见:Understanding UIScrollView

    view.frame.origin是view在其父view的坐标系统内的位置(由view.center配合view.layer.anchorPoint、view.bounds.size导出),而view.frame.size就是view.bounds.size。

  • UIWindow含多个subview时,只有其root view才能自动处理autorotation。

  • [viewController presentViewController:animated:completion:]时会把presentingViewController.view加到viewController.view.superview中,这superview是个隐藏节点UITransitionView

  • ContainerViewController(iOS5起引入)继承UIViewController,添加childViewController时:

      1. [self addChildViewController:childViewController]; 
      // 1. will send [childViewController willMoveToParentViewController:self];  
      2. [self.view addSubview:childViewController.view];
      3. [childViewController didMoveToParentViewController:self];

    移除childViewController时:

      1. [childViewController willMoveToParentViewController:nil];
      2. [childViewController.view removeFromSuperview];
      3. [childViewController removeFromParentViewController];
      // 3. will send [childViewController didMoveToParentViewController:nil];

    参考:Implementing Container Containment – Sliding Menu Controller

  • 要是创建项目时没有勾选Use Storyboard,从程序中加载storyboard要在application:didFinishLaunchingWithOptions:中:

      // self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
      UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
      self.window.rootViewController = [storyboard instantiateInitialViewController];
      // [self.window makeKeyAndVisible];

    注:若打开第1行注释重设self.window,iOS8中需要在[YourApp]-Info.plist中去掉UIMainStoryboardFile值。

  • scrollViewWillEndDragging:withVelocity:targetContentOffset:targetContentOffset参数可以决定scrollView滑动停止在哪儿

RunloopMode

Runloop只会处理其mode中输入源:

  • default mode包含大部分输入源、定时器等
  • tracking mode处理UI事件(如scrollView滚动/减速等)
  • common modes = default mode | event mode

定时器运行在default mode,scrollView滚动等UI事件肯能block定时器准时执行,可把定时器设成common modes:

1
2
3
4
5
6
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 
target:self
selector:@selector(handleTimer:)
userInfo:nil
repeats:NO]
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
1
2
3
4
[self performsSelector:@selector(performDelayedWork)
withObject:nil
afterDelay:1.0
inModes:@[NSRunLoopCommonModes]];

参考:

  • 计算二进制中1的个数

    实质是求该二进制数与0的hamming距离,详见wiki。其分治解法 按位分片 统计1的个数:

      count 1s in each  2-bit piece
      count 1s in each  4-bit piece
      count 1s in each  8-bit piece
      count 1s in each 16-bit piece
      count 1s in each 32-bit piece

    The best solutions known are based on adding counts in a tree pattern.

      /*
       * For example, to count the number of 1 bits in the 8-bit binary number A=01101100
       * | Expression            | Binary   | Decimal | Comment                          |
       * | A                     | 01101100 |         | the original number              |
       * | B = A & 01010101      | 01000100 | 1,0,1,0 | every other bit from A           |
       * | C = (A>>1) & 01010101 | 00010100 | 0,1,1,0 | remaining bits from A            |
       * | D = B + C             | 01011000 | 1,1,2,0 | # of 1s in each 2-bit piece of A | 
       * | E = D & 00110011      | 00010000 | 1,0     | every other count from D         |
       * | F = (D>>2) & 00110011 | 00010010 | 1,2     | remaining counts from D          |
       * | G = E + F             | 00100010 | 2,2     | # of 1s in each 4-bit piece of A |
       * | H = G & 00001111      | 00000010 | 2       | every other count from G         |
       * | I = (G>>4) & 00001111 | 00000010 | 2       | remaining counts from G          |
       * | J = H + I             | 00000100 | 4       | the final answer                 |
    
      typedef unsigned int uint32;
      const uint32 m1  = 0x55555555; //binary: 0101...
      const uint32 m2  = 0x33333333; //binary: 00110011..
      const uint32 m4  = 0x0f0f0f0f; //binary: 0000111100001111..
      const uint32 m8  = 0x00ff00ff; //binary: 00000000111111110000000011111111
      const uint32 m16 = 0x0000ffff; //binary: 00000000000000001111111111111111
    
      int population_count(uint32 x) {
          x = (x & m1 ) + ((x >>  1) & m1 ); //put count of each  2 bits into those  2 bits 
          x = (x & m2 ) + ((x >>  2) & m2 ); //put count of each  4 bits into those  4 bits 
          x = (x & m4 ) + ((x >>  4) & m4 ); //put count of each  8 bits into those  8 bits 
          x = (x & m8 ) + ((x >>  8) & m8 ); //put count of each 16 bits into those 16 bits 
          x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits 
          return x;
      }

    平时常见的解法:

    This better when most bits in x are 0.

      int population_count(uint32 x) {
          int count;
          for (count = 0; x; count++) {
             x &= x-1; // zero out the least significant bit
          }
          return count;
      }

least significant bit

  • -x # == ~x+1, 末位1前头的按位取反

  • x&-x # 末位1

  • x-1 # 末位1起按位取反

  • x&(x-1) # 去掉末位1

  • (x&(x-1)) == 0 # 是2的幂

most significant bit

  • 首位1起全置1

      inline uint32 set_bits_from_msb(uint32 x) {
          x |= (x >> 1);
          x |= (x >> 2);
          x |= (x >> 4);
          x |= (x >> 8);
          x |= (x >> 16);
          return x;
      }
  • 首位1

      uint32 msb(uint32 x) {
          x = set_bits_from_msb(x);
          return x & ~(x >> 1);
      }
  • 下个最近2的幂

      uint32 next_largest_power_of_2(uint32 x) {
          x = set_bits_from_msb(x);
          return x+1;
      }
  • log2的floor

      uint32 floor_of_log2(uint32 x) {
          x = set_bits_from_msb(x);
          return population_count(x) - 1;
      }

Reference

  • sting interpolation: "#{var_name}"
    • 双引号""转义其中的内容,单引号‘’不转义
  • symbol是种不可变串,全局只创建一份,因此创建和比较等性能更高
    • :"hello"等同于:hello
    • symbol和string互转使用::hello.to_s"hello".intern
  • 函数中的yield(...)会调用函数调用时提供的block,相当于把函数的一部分功能委派给这个未来的block,相当于函数多了一个叫yield的block参数
    • 如果这个block参数是可选的,函数中要用yield(…) if block_given?
  • 将01串表示的二进制转成字节数组: ['1100110011'].pack('B*').unpack('C*')
    参见:string的pack和unpack

nokogiri

  • 取节点属性node['attr-name'],取某子节点内容node.at_xpath('elem-name').text
    • 方法.at_xpath()返回第一个节点,.xpath()返回节点数组

参考

在命令执行失败后退出:

1
cmd || exit 1

子shell(把命令由括号括起来):

1
cmd | (cmd1; cmd2; cmd3) | cmd4

子shell中的cmd1可以改变工作目录,而不影响外部的cmd4

捕获键盘中断:

1
2
3
4
5
6
trap control_c SIGINT

control_c() {
echo `abort: ctrl-c`
exit
}

获得当前执行脚本的目录($0就是当前执行脚本):

1
$(dirname $0)

if测试:

1
2
3
4
5
if [ ! -d $A_DIR ]; then
mkdir -p $A_DIR
else
echo 'exists dir: '$A_DIR
fi

for循环:

1
2
3
for f in *; do 
echo $f;
done

xargs

1
ls *.old | xargs -n1 -I{} echo mv {} {}.new
  • -n N,将输入参数规整成一行N个的形式(原先的输入参数由' '\n分隔,每行个数可能不一样)
  • -I {}:由-I指定的字符串{}在每一次执行中都会被替换相应的输入参数
1
find . -type f -name "*.txt" -print0 | xargs -0 rm -f
  • find -print0指定输出的文件名以\0结尾
  • xargs -0指定\0作为参数分隔符,而不是默认的' '\n

只要把find的输出作为xargs的输入,就必须加-print0-0参数,确保以\0来分隔输出。否则,文件名中包含的空格' '会被xargs会误以为是分隔符。

1
2
cat args.txt | ( while read arg; do cat $arg; done)
# 等同于 cat args.txt | xargs -I{} cat {}

为对同一个参数执行多条命令,使用while循环,将cat $arg换成任意数量的命令

  • C99的inline用法和C++稍有不同,portable用法见Inline Functions In C

    the xxx.h file:

      #ifndef INLINE
      # if __GNUC__ && !__GNUC_STDC_INLINE__
      #  define INLINE extern inline
      # else
      #  define INLINE inline
      # endif
      #endif
      INLINE int max(int a, int b) {
          return a > b ? a : b;
      }

    the xxx.c file:

      #define INLINE
      #include "xxx.h"
  • 参数个数不同的macro重载

  • 由于内存对齐,定义struct时应将成员从大到小排列最省空间

  • 用[jarjar][]修改jar中的包名或类名:

    java -jar jarjar.jar process <rulesFile> <inJar> <outJar>

    每条rule一行,包名或类名匹配多条rule时只取最上一条,rule格式:

    rule <pattern> <resule>

    替换包名用:rule old.package.** new.package.@1
    替换类名用:rule **OldClass @1NewClass

[jarjar]: [http://code.google.com/p/jarjar/wiki/CommandLineDocs]

  • 用[jd-gui][]查看jar包中.class的代码

[jd-gui]: [http://java.decompiler.free.fr/?q=jdgui]

  • xCode中开启c++11支持:

      Build Settings -> C++ Language Dialect 选 C++11
                     -> C++ StandardLibrary 选 libc++
  • lambda:

    语法: [capture-list](args-list) ->return-type {…}

    可转类型:function<return-type (args-type-list)>

    • lambda的类型是实现了operator()的匿名类型,可转成std::function<>,但不是std::function<>,因此

        template<typename ...Args>
        void call(Args&& ...args) { std::cout << "call 1"; }
        template<typename ...Args>
        void call(std::function<void ()>, Args&& ...args) { std::cout << "call 2"; }

      调用call([](){});输出call 1,而不是call 2

      要输出call 2,函数调用时要显式将lambda参数转成std::function类型:

        call(std::function<void ()>([](){}))
  • range-based for:

    遍历map:

      for (auto& kv : aMap) {
          cout << kv.first << " has value " << kv.second << endl;
      } 

    非成员函数的begin()end():要定义在CustomClass所在的namespace中

  • 取tuple中第i个元素:std::get<i>(tuple)

  • string和number间互转:

      string to_string(int val);
      string to_string(float val);
      …
      int stoi(const string& str, size_t *idx=0, int base=10);
      float stof(const string& str, size_t *idx=0);
      …
  • unique_ptr使用自定义deleter,例如:

      template<typename T>
      using cc_unique_ptr = std::unique_ptr<T, std::function<void(void *)>>;
      template<typename T>
      cc_unique_ptr<T> make_unique(T *ptr) {
          if (ptr) ptr->retain();
          auto deleter = [](void *p) {
              auto ptr = static_cast<T *>(p);
              if (ptr) ptr->release();
          };
          return cc_unique_ptr<T>(ptr, deleter);
      }
  • 为enum指定underlying type: enum E : short { kValue1, kValue2, ... },然后可以使用forward declare: enmu E : short;

  • 强类型enum用关键字enum class声明,其枚举值不会暴露在surrounding scope

      enum class Options {None, One, All};
      Options o = Options::All;
  • 可给函数方法添加后置标识符override(指明某函数重载父类函数)和final(指明某函数不能被子类重载)

  • 支持正则表达式<regex>

参考

由于c++没有反射,CocosBuilder配合cocos2d-x比cocos2d繁琐。

  • 设置node的Custom Class,要为该类写loader,该loader继承自cocos2d::extension::CCXXXLoader
  • memberVar和menuItem的target可以是doc rootowner
  • 设置memberVar,会在load过称中调用target的onAssignCCBMemberVariable,即target要实现cocos2d::extension::CCBSelectorResolver
  • 设置menuItem的callback,会在load过称中调用target的onResolveCCBCCMenuItemSelector,即target要实现cocos2d::extension::CCBSelectorResolver

Case 1:ccb中的CCLayer关联代码中的GameLayer

  1. ccb中CCLayer设置Custom classGameLayer

  2. 代码中为GameLayer添加loader类class GameLayerLoader : public cocos2d::extension::CCLayerLoader,为该loader实现createCCNode()方法。 若在ccb中给该CCLayer设置了custom property,还得在loader类中实现相应的onHandlePropTypeXXX()方法。

  3. 在提供给CCBReader的CCNodeLoaderLibrary中注册loader对象

     auto loaderLibrary = cocos2d::extension::CCNodeLoaderLibrary::newDefaultCCNodeLoaderLibrary();
     loaderLibrary->registerCCNodeLoader(“GameLayer”, nodeLoader);            
  4. 代码中可选实现class GameLayer: public cocos2d::extension::CCNodeLoaderListener

  • 该过称繁杂,可写util函数,然后:

      auto layer = ccb::layer<GameLayer>(/*init params*/);

    如GameLayer含Custom Class子类CustomMenuItemImage,可用:

      auto loaderLibrary = ccb::defaultCCNodeLoaderLibrary<GameLayer, CCLayerLoader>();
      ccb::registerCCNodeLoader<CustomMenuItemImage, CCMenuItemImageLoader>(loaderLibrary);
      auto layer = ccb::layer<GameLayer>(loaderLibrary/*, other init params*/);

Case 2. ccb中的CCMenu关联代码GameLayer的buttonMenu

  1. ccb中CMenu设置Code connections:选Doc root varmemberVar

  2. 代码中class GameLayer : public cocos2d::extension::CCBMemberVariableAssigner,并实现:

     bool onAssignCCBMemberVariable(cocos2d::CCObject * pTarget, cocos2d::CCString * pMemberVariableName, cocos2d::CCNode * pNode) {
         CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "_buttonMenu", CCMenu *, _buttonMenu);
         return false;
     }

  • 可定义简化宏:

      #define CCB_MEMBER_VAR(__TYPE__, __VAR__) CCB_MEMBERVARIABLEASSIGNER_GLUE(this, #__VAR__, __TYPE__, __VAR__) 

    然后onAssignCCBMemberVariable()中可写

      CCB_MEMBER_VAR(CCMenu *, _buttonMenu);

Case 3:ccb中CCMenuItem调用代码GameLayer的buttonCallback

  1. ccb中的CCMenuItem设置:selectorbuttonCallbackTargetDocument root

  2. 代码中class GameLayer : public cocos2d::extension::CCBSelectorResolver,并实现:

     cocos2d::SEL_MenuHandler onResolveCCBCCMenuItemSelector(cocos2d::CCObject * pTarget, cocos2d::CCString * pSelectorName) {
         CCB_SELECTORRESOLVER_CCMENUITEM_GLUE(this, "buttonCallback", GameLayer::backButtonCallback);
         return NULL;
     }
     cocos2d::extension::SEL_CCControlHandler onResolveCCBCCControlSelector(cocos2d::CCObject * pTarget, cocos2d::CCString * pSelectorName) {
         return NULL;
     };

  • 可定义简化宏:

      #define CCB_CCMENUITEM_SELECTOR(__CLASS__, __FUNC__) CCB_SELECTORRESOLVER_CCMENUITEM_GLUE(this, #__FUNC__, __CLASS__::__FUNC__)
      #define CCB_CCCONTROL_SELECTOR(__CLASS__, __FUNC__) CCB_SELECTORRESOLVER_CCCONTROL_GLUE(this, #__FUNC__, __CLASS__::__FUNC__)  

    然后onResolveCCBCCMenuItemSelector()中可写

      CCB_CCMENUITEM_SELECTOR(GameLayer, backButtonCallback);

参考