Objective-C 类方法 load 和 initialize 的区别

过去两个星期里,为了完成一个工作,接触到了NSObject中非常特别的两个类方法(Class Method)。它们的特别之处,在于iOS会在运行期提前并且自动调用这两个方法,而且很多对于类方法的规则(比如继承,类别(Category))都有不同的处理。

而因为这两个方法是在程序运行一开始就被调用的方法,我们可以利用他们在类被使用前,做一些预处理工作。比如我碰到的就是让类自动将自身类名保存到一个NSDictionary中。

先来看看NSObject Class Reference里对这两个方法说明:

+(void)initialize

The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.

+(void)load

The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.
The order of initialization is as follows: 阅读全文 >>

Xcode 4 中使用自定义字体,非 Outlet 方式

在 Xcode 3 的时代,如果要使用自定义字体,只要把字体安装到系统中,就能够在 Interface Builder  中字体列表中选择到,当然需要把字体文件加到项目中,并且在 Xxx-Info.plist 文件中列举出来。

而到了 Xcode 4 后,要用个自定义的字体反而更难了,字体安装到了系统中,它也不会出现在 Interface Builder 中的字体列表,必须用 setFont 为需要的控件应用自定义字体,这是一个浩大的工程,要么遍历控件,要么就得为控件与 IBOutletCollection 关联起来。

难道是 Apple 不希望看到应用中各色字体令人眼花缭乱的景象,所以制造点障碍。也不知道新的  Interface Builder 中的字体列表由哪得来的,是否能把自己的字体出现在该列表中呢?这个嘛,有点难。

既然不想使用 setFont 方法对 Xib 中的众多的控件一一设置字体,那就得另寻 Hack 方法了,幸好有了 FontReplacer 的方法。FontReplacer 是为 UIFont 做的一个 Category,它的实现原理是利用 method_exchangeImplementations 机制把 UIFont 的 fontWithName:size:fontWithName:size:traits: 换成自定义的方法,然后在自定义的方法中按照预设置的字体映射,当遇到某个系统字体 A 时,换成自定义的字体  Custom-A。

而你所要做的就是在 Interface Builder 中寻找字体,自己记住当选用了 系统字体 A,运行时会是 Custom-A 字体,系统字体 B 运行时会是 Custom-B,根据需要自己映射就行。一般方便起见,我们会选择一种字体(Family),然后用它的 Style 来与我们的自定义字体对应。这样只需要 Interface Builder 里像往常一样设置字体就行了,从而达到一劳永逸。 阅读全文 >>

使用 sbt 的 np 插件自动创建目录结构和 build.sbt 文件

通常我们要创建一个 sbt 项目需要经历以下几步:

$ mkdir testscala && cd testscala      #创建项目目录 testscala,并进入该目录
testscala $  mkdir -p src/{main,test}/scala   #创建源文件目录结构,可能还需要 mkdir -p src/{main,test}/resources
$ vi build.sbt # 填入基本的信息 (name, organization, version)
$ sbt

这一看怎么这么复杂啊,特别是习惯了 Maven 的同学们更会这么觉得。Maven 不仅可应用各种模板来创建不同类型的工程,完整的工程目录结构都有了。

那么 sbt 能不能做到自动创建目录和配置文件呢,答案是: 本身不能。你可以基于一个项目模板目录结构拷来拷去。前面说是 sbt 本身不能,要知道 sbt 是一个插件化更彻底的玩意,如今的 0.12.2 的 sbt.zip 才 1M 大小,其余全赖插件帮你插来插去了。

因此,我们找来的 np(New Project) 插件 https://github.com/softprops/np 帮我们创建工程的目录结构和 build.sbt 文件。它能让我们创建项目的过程简化为以下两步操作:

$ mkdir testscala && cd testscala   #这一步还是不能少
$ sbt
> np name:testscala org:cc.unmi   #这样 testscala 下 {main.test}/{scala,resources} 和  build.sbt 文件都有了

np 的安装及使用可以参考:https://github.com/softprops/np,就是下面的步骤: 阅读全文 >>

Objective-C 实现自己的 Subscripting Methods 下标方法

在前一篇 Xcode 4.4/4.5 新特性 / LLVM 4.0 新语法 提到了 Xcode 4.4 之后对数组和字典的操作可以用下面的方式:

id value = array[i];
array[i] = newObj;
id value = dictionary[@"key"];
dictionary[@"key"] = newObj;

上面几行语句实际调用的方法分别对应如下:

NSArray : - (id)objectAtIndexedSubscript: (NSUInteger)index;
NSMutableArray : - (void)setObject: (id)obj atIndexedSubscript: (NSUInteger)index;
NSDictionary : - (id)objectForKeyedSubscript: (id <NSCopying>)key;
NSMutableDictionary : - (void)setObject: (id)anObject forKeyedSubscript: (id <NSCopying>)aKey;

所以,如果你希望自己定义的类可以支持 obj[i] 和 obj[@"key"] 的方式来读取和设置值的话,就可以实现上面相应的方法,只是要处理好你自己参数和结果类型。现在就来看看下面自定义类 TestClass 的实现和执行效果: 阅读全文 >>

Xcode 4.4/4.5 新特性 / LLVM 4.0 新语法

跟着苹果干的好处就是,有什么新特性就尽快的就享用它们,这非常符常程序员们的学习心态。不像国内的某些技术领导,他们很领会某种舞台上的精髓 -- 稳定压倒一切 ,JDK 1.6 都出来了,还不少项目还牢牢固守在 JDK 1.3 上,程序员真是无言而又苦 B 啊。当然你在使用  Objective-C 时能时刻得到更新也得利于 Obj-C 完全由 Apple 主导,免了不少口水,瞧瞧 C++ 到 C++11,历时 13 年啊。

OK,言归正传,Xcode 当前版本 4.5.2,来看下自 4.4 以来它给我们带来了什么语言上的新特性。每个版本有新特性时一定要看官方的  What's New in Xcode。这么说来 Xcode 4.4 也算是个关键性的版本更新。

@ 不光可用于创建 NSString,还简单化了 NSNumber,NSArray 和  NSDictionary 对象的创建,一一来看。

@ 创建 NSNumber 实例

从 Xcode 4.4 起,无段  [NSNumber numberWithInt: 10] 这么写了,@10 就完事,下面是各种数值类型的 NSNumber 创建方式: 阅读全文 >>

Scala 2.10.0 新特性之字符串插值

Scala 在处理字符串方面终于也与时俱进了,引入了字符串插件,这在许多脚本语言中就早已这么做了。有了这个特性,字面量字符串和变量连接可以不需要总是用加号进行丑陋的连接了。有些脚本语言会是只对双引号字符串解析其中的变量,单引号的不会,当然Scala 是要区分字符串和字符类型。

直白的讲字符串插值就是,val name="Unmi"; val greeting=s"Hello $name"; greeting 的值就是 "Hello name"。产生效应的就是那个 s 方法。

Scala 2.10.0 为我们提供了三种字符串插值的方式,分别是 s, f 和  raw。它们都是定义在 StringContext 中的方法。

s 字符串插值器

可以解析字符串中的变量,可以调用方法,还能进行计算。实际调用的是  StringContext 中的  s 方法。 阅读全文 >>

Scala 2.10.0 新特性之动态属性、方法

最早的介绍 Scala 语言的书都是以 Scala 的静态类型系统为傲。Scala 也算是个脚本语言,却不像其他许多脚本语言那样类型是动态的,只有执行时才确定,而 Scala 在执行前就确定了类型,比如依赖于比 Java 更强大的类型推断行为。

静态类型不光是变量类型是确定的,还有比如在使用  qual.sel 时,sel 这个属性或是方法(Scala 的访问一致性,属性和方法有时候并没有那么大的区别)必须在 qual 的类型中声明了的。

Scala 思考再三还是加入了 Dynamic Types,这个特性在 Scala 2.9 中是试验性的,必须用 -Xexperimental 进行开启,到了 Scala 2.10.0 中,只有代码中 import scala.language.dynamics 就可用了,或是编译时加 -language:dynamics 选项。

虽然 Scala 2.10.0 加进了 Dynamic Types 特性,但 Scala 仍然是静态类型的语言,因为在编译器同样会检查多出来的类型。

有了 Dynamic Types 之后,Scala 又可更 DSL 了,方法名的动态上可以让它随时包括深刻的业务含义。相比 Java 的 DSL 的能力就太逊了,我们几乎无法在 Java 面前提 DSL 这回事。 阅读全文 >>

使用 Jackson 把 Java 对象转换成 JSON 字串

原来 Java 项目中用的 JSON 组件库主要是 Gson 和 json-lib,Gson 算是很错的库,json-lib 略显寒碜。好啦,最近 Play 2.x 中弃用了 Gson 而采纳了 Jackson,所以现在就来打探一下 Jackson,踩个点吧。

Jackson 号称非常高的性能,听说比另两位兄弟 Gson 和 json-lib 高出一大截,我没有亲测,可是有心人做了,看这个链接 两款JSON类库Jackson与JSON-lib的性能对比(新增第三款测试) 中的数据。2010 年 8 月份的测试结果,不知现在随着版本的变更是否仍然保持着这种悬殊。

通常我会在把文章开头塞丰满,做足前戏,并不是因为在天涯混习惯了的缘故,况且我在天涯总是讷于言的; 在这里,自己的地盘自己作主,不会有要求码足多少字才能发表的自虐性需求,仅仅是让本文在主页上显示时的的概要不空洞而已,可以简单粗暴的称之为废话。

了了,先了解 Jacson 最贴近实际应用场景的应用,即 Jackson 怎么把一个 Java 生成对应的 JSON 字符串,看看前面的文字有这么多了,直接上一段代码吧,而后再慢慢假设与分解: 阅读全文 >>

Scala 2.10 新特性之使用隐式类进行类型隐式转换

在 Scala 2.10.0 之前类型的隐式转换必须通过隐式方法来完成,现在的 Scala 可以用 implicit class 来声明类,并且它的主构造器 (Primary Constructor) 只有一个参数时,就可以用来把参数隐式转换成该类型。

能理解上面什么意思,知道怎么用隐式类吗? 就上面那句话,我自己都不知道在说什么。

首先要知道 Scala 先前是怎么依据隐式方法进行类型的隐式转换,其次又何谓主构造器呢? 关于 Scala 2.10.0 的 implicit class,官方的解释在这里 http://docs.scala-lang.org/sips/pending/implicit-classes.html 再多说只能让大家更摸不到头,实例演示是王道:

scala-2.10.0-implicit-class 阅读全文 >>

Play 2.0 中文资料 - 应用程序全局对象

应用全局设置

全局对象

在你的项目中定义一个 Global 对象,它为你处理应用的全局设置. 这个对象必须定义在默认包中(即不带 package 声明的包).

小贴士: 你也可使用 application.global key 值在在配置中指定自己的 GlobalSettings 实现类名称.

Unmi 注: GlobalSettings.scala 放在 app 目录比较合适,Scala 的 package 层次与目录并不存在严格的对应关系。

要说呢,不带 package 声明的写法确实不是什么好习惯,因为其他带 package 声明的类无法使用它。见:为何Java的有包名的类不能引用默认包中的类。这就对了,本来这个全局对象就不是给别的类用的 - Don't call me, I'll call you。

勾住应用启动和停止事件

你可以覆盖方法 onStartonStop,它们会在应用的生命周期启动,停止时得到通知: 阅读全文 >>