第九块石头

不要在 Initializer 和 Dealloc 方法中使用 Setter 和 Getter

| Comments

今天和同事在争论 iOS 中,init 方法使用 self.xxx = xx; 是否合适的问题,以下面代码为导火索:

1
2
3
4
5
6
 - (id)initWithIconImage:(UIImage *)iconImage loadingImage:(UIImage *)loadingImage {
     if (self = [self init]) {
        self.iconImage = iconImage;
     }
     return self;
 }

他的观点是 self.iconImage = iconImage; 应该改为 _iconImage = [iconImage retain];,理由是担心内存被强制释放,那么 self.iconImage = xxx 将会导致崩溃。

我的观点是 if (self = [self init]) 已经判断内存是否初始化正常了,不需要考虑 self 内存异常问题,所以不需要专门改成 _iconImage = [iconImage retain];,当然这么写也没错。

看似一个挺无聊的争论,但是最后一个技术专家站出来,贴了个 Apple 的文档: Don’t Use Accessor Methods in Initializer Methods and dealloc

The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc.

看来我们争论的方向都错了,init 里面不用 setter 不是内存问题,而是 setter 可能会触发其他的逻辑,例如重写的 setter 方法或者 KVC,将可能调用其他还没来得及 init 的变量,最终导致不可预计行为!

颠覆了以前的使用方法~~

不禁思考到其他语言,似乎都存在这种问题~~

Nagios4+Nginx+Ganglia配置

| Comments

本文参考 Nagios on nginx & Ubuntu 12.04

最近需要在服务器上配置一个Nagios用于检测服务器的异常情况并报警,我们的服务器已经搭建好Ganglia,Nagios主要用于报警通知。现在最新的Nagios的版本已经到了4.0.2, 而apt-get只有nagios3, 并且似乎还自动装了apache, 这不是我们需要的。看了一些资料后得知一般情况下 nagios 应该安装在 /usr/local/nagios 文件夹下,这样安装插件的时候会少很多麻烦。 而按照ubuntu的习惯,配置文件我决定放在 /etc/nagios 文件夹里。

首先介绍一下安装上面的配置需要了解的nagios的各个文件夹或者文件的用途

/usr/local/nagios/libexec 是nagios插件存放的位置, nagios配置中有个USER1指的就是这个文件夹的路径
/etc/nagios/nagios.cfg 是nagios的主配置文件,它会引用到很多其它的配置文件,比如下面说到的objects文件夹下的所有配置
/etc/nagios/objects 文件夹里有可以配置命令的 commands.cfg 可以配置联系方式的 contacts.cfg
/etc/nagios/servers 这是我需要的一个文件夹,用于配置需要监控的服务的,需要在 /etc/nagios/nagios.cfg里取消掉相应行的注释

下面是我的安装配置过程

安装依赖

1
apt-get install libperl-dev libpng12-dev libgd2-xpm-dev build-essential php5-gd wget libgd2-xpm

UIScrollView 和 Swipe 手势的识别问题

| Comments

UIScrollView 用得相当普遍,衍生出来的 UITableView 也用得不少。最近有人问我,当给 UIScrollView 加上左右滑动手势 UISwipeGesture 时,感觉好难滑动,必须要很平的左右划才会触发 Swipe,否则都是触发 UIScrollView 的滚动事件。

这时候,我们会想到,不需要那么水平的滑动就好了,例如以滑动45度为分割线,滑动轨迹与水平线夹角在正负45度,都认为是水平滑动,超过45度,就认为是垂直滚动。

看上去好像可以做。那么我们就要拦截发送给 UIScrollView 的手势——重载 UIScrollView 的手势响应方法:

1
2
3
4
5
6
7
8
9
10
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        CGPoint point = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:self];
        if ((fabs(point.y) / fabs(point.x)) < 1) { // 判断角度 tan(45),这里需要通过正负来判断手势方向
            NSLog(@"横向手势");
            return NO;
        }
    }
    return [super gestureRecognizerShouldBegin:gestureRecognizer];
}

重载 UIScrollView,用这个新的对象,并且适当的调整其中的角度,来优化 APP 中的手势灵敏度。

iOS5 上 Gesture 和 UIButton 手势冲突解决方法

| Comments

在一个 View 上面加一个 UIButton,指明一个 Action,很简单;在这个 View 上面加一个 Tap 手势,恩,也很简单。但是两者一起加,当我们点击 Button 时候,触发哪个呢?

经过测试,当系统是 iOS5 及以下时,响应 Tap 手势;当系统是 iOS6 及以上时,响应 Button 事件!!

那我们一般期望是什么行为呢?估计很多人都是想和 iOS6 那样,优先响应 Button 的事件。

让 iOS5 及以下响应 Button 事件的方法:

1
2
3
4
5
6
7
8
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {

    // Disallow recognition of tap gestures in the control.
    if (([touch.view isKindOfClass:[UIControl class]])) {
        return NO;
    }
    return [super gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch];
}

重载View,复写上面的方法即可。

设置Ubuntu程序开机启动

| Comments

以前配置系统启动项都是拿来能用就行,对于其中规则一点都不熟悉,这两天恰好遇到几篇通俗易懂的介绍,受益匪浅。

涉及到开机启动的配置文件在下面几个文件夹里:

  • /etc/init.d 放着程序启动关闭重启的脚本
  • /etc/rc(0-6).d里的文件都是/etc/init.d文件下的软链接,rc0.d rc1.d … 分别对应着linux不同的 runlevel
  • /etc/default 里的文件添加一层控制是否启动某个程序的变量

手工设置开机启动程序的步骤是这样的:

  1. /etc/init.d里写一个启动脚本, 比如:some_program(启动脚本模板后面提供)
  2. 使用 sudo update-rc.d some_program defaults 设置开机启动. 可以看到这个命令的工作是把 /etc/init.d/some_program 软链接到了 /etc/rc(0-6).d 里。
  3. 现在重启系统, some_program 就会启动了。

中国地图坐标偏移算法整理

| Comments

最近被天朝的火星坐标搞得头昏脑胀的,真不知道这个所谓的火星坐标到底是防国外军事打击的呢,还是为难我们这些苦逼的开发人员的~真的能防止国外非法使用吗?难道美国国防部也用 Google 地图???

暂且不考虑这个不是我等草民能考虑的问题,先看看如何解决这种火星坐标导致的地图坐标不匹配的问题~

首先,在天朝,所有的坐标都必须经过国家测绘局进行偏移,这里的坐标,包括某个景点的坐标,甚至是整张地图的坐标(地图是由多个建筑、地形坐标构成)!!而偏移算法目前在正规渠道来说是保密的,根据小伙伴们的观察,偏移量是不一定的,我就在想,一个正方形的建筑,经过这种偏移,会不会偏出一个梯形出来…………

既然地图是偏移的,并非真实的地图,那么我们往地图上面标注景点或者建筑的时候,甚至标注用户当前位置时,就不能直接用 GPS 输出的真实值,而是得将 GPS 坐标也进行相同算法的偏移,然后放在地图上就感觉没偏移了——大家一起做漂移~

所以市面上,就出现了一种现象,水货的 GPS 设备(有的手机也是),做定位的时候输出真实值,而中国市面上的地图,例如 Google 地图、百度地图等,都是必须遵守国家测绘局规定进行偏移的。所以出现了定位不准的问题。而行货 GPS 设备,由于是正规渠道,里面内置了偏移芯片,所以输出的定位坐标是偏移过的,放在 Google 地图上面就正常了!

Adobe CS6 官方简体中文原版下载收集

| Comments

不知道为啥,Adobe 官方下载的时候没有中文的选择?例如下载 PS 的地址:http://www.adobe.com/cfusion/tdrc/index.cfm?product=photoshop&loc=cn,只能选择英文……

上网找了一下,官方是有中文版本下载的,只是不知道什么原因不显示……这里整理一下:

Adobe Creative Suite 6 分为 Design StandardDesign & Web PremiumProduction PremiumMaster Collection 四个版本,其实说白了,就是各种独立软件的打包……

根据官方说明,可以看到下面这个表:

使用rsync同步不同机器上的文件

| Comments

实验系统:Ubuntu

我根据文章rsync 简明教程试验成功后,发现Ubuntu自带有启动rsync守护程序的功能,于是决定使用Ubuntu自带的开机启动rsync方式,查看/etc/init.d/rsync里的代码可以知道它指定了rsync配置文件的路径:/etc/rsyncd.conf,如果没有这个路径,rsync daemon不会启动。

于是创建一个/etc/rsyncd.conf 在里面填上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uid = root
gid = root
use chroot = yes
max connections = 10
syslog facility = local5
pid file = /var/run/rsyncd.pid
log file = /var/log/rsyncd.log

[rsyncsrc]
        path = /home/vagrant/rsyncsrc
        list = yes
        ignore errors = yes
        auth users = admin
        secrets file = /etc/rsync/rsync.secrets
        comment = test rsync source

上面的参数中,我对use chroot印象比较深刻,如果use chroot 指定为true,那么rsync在传输文件以前首先chroot到path参数所指定的目录下。这样做的原因是实现额外的安全保护,缺点是需要root权限,并且不能备份指向外部的符号连接所指向的目录文件。默认为true(摘抄来的)

使用 drawInRect 绘图时丢失清晰度解决方法

| Comments

有一个小需求,就是需要动态修改一张图片上面的数字,当然不能事先准备那么多数字的图片,所以就需要动态的在一个图片上面画数字。

需求很简单,我首先这么实现:

1
2
3
4
5
UIImage *image = [UIImage imageNamed:@"assemble-a"];
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0,0,image.size.width,image.size.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

画出来的图怎么看都模糊,刚开始没在意,以为就是这样的……

然后给美女设计师看效果,直接回了一句——怎么这么糊……

让 XCode 静态库不需要使用者 Link Framework

| Comments

有些情况下需要创建静态库给别人用,如果静态库里要引用很多 framework ,会给别人带来麻烦;特别是那些要设置为 optional 的 framework ,很容易导致在低版本系统上崩溃。

一般情况下,静态库中大部分的 Objective-C framework 可以通过下面的方法来避免让使用者 link 。

首先,要测试一个 framework 是否已经被 link 到了程序中, 对于 Objective-C 的库可以通过NSClassFromString检测出来;
如果那个 framework 没有被 link 进来,则使用dlopen加载 framework;
使用 framework 中的类时,依然需要通过NSClassFromString

整个过程所需的代码大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>
#include <dlfcn.h>

if (!(NSClassFromString(@"ASIdentifierManager"))) {
  dlopen("/System/Library/Frameworks/AdSupport.framework/AdSupport", RTLD_LOCAL);
}

NSObject *manager = [NSClassFromString(@"ASIdentifierManager") performSelector:NSSelectorFromString(@"sharedManager")];
if (manager) {
  [manager performSelector:NSSelectorFromString(@"advertisingIdentifier")];
  ...
}

对于 Objective-C 的类,使用起来毫无压力,有时还需要使用到 framework 中的常量。如果用到的常量不多,可以在dlopen后马上把需要的常量导进来,供之后使用。

1
2
void *handle = dlopen("core telephony framework path", RTLD_LOCAL);
NSString *_k_CTCallStateDialing = *(void **)dlsym(handle, "CTCallStateDialing");

还有一些我也不知怎么导入,例如枚举值,对于系统的 framework 我对它的稳定性比较有信心。通过看代码可以看出枚举值,可以直接使用枚举值对应的整数

1
2
3
4
// 1 == MessageComposeResultSent
if (result == 1) {
  ....
}