2009年3月2日星期一

关于创建一个类

创建一个类的时候注意,凡是class method,在返回值前面需要加一个“+”号,
如果是instance method正好相反,在返回值前面加一个“-”号。

在同一个类的声明中,class method 可以跟instance method重名,另外method也可以与某个instance veriable重名,这种情况比较常见,往往用于获取这个instance veriable的值的方法中。

在声明返回值的时候可以用标准C 的语法将返回类型转换成需要的类型,比如
- (float)radius;
另外,如果这个method带有参数,且参数也需要转换类型的话,这往往是有必要的,因为不能保证调用参数的时候传入的参数类型就正好是相符的。比如
- (void)setRadius:(float)aRadius;

注意,在定义method的时候如果不显示的说明返回值或者传入参数的类型,那么缺省的类型就是id,

如果method有一堆参数的话,如此定义。
- makeGroup:group, ...;
- (void)setWidth:(float)width height:(float)height;

Class Name的用途

在Objective-C中,Class Name有两种用途,第一是作为类型的说明,用来生成一个此Class的变量的声明。比如

Rectangle *anRect;

另外一种用途是用作Class Object,但是注意,用作Class object的时候一定是要放在一个message expression中,当作一个receiver来用的时候方才可以。比如

if ( [anObject isKindOfClass:[Rectangle class]] )
而不能写成
if ( [anObject isKindOfClass: Rectangle] )

2009年3月1日星期日

创建实例

id myRect;
myRect = [Rectangle alloc];

alloc方法动态分配空间并且进行初始化,它会把除isa以外的其他所有的都弄成0。
但是为了令实例更可用,一般在alloc之后会紧接着调用一个init, 如下。

myRect = [[Rectangle alloc] init];

metaclass object

之前说到了Class object 它是compiler生成的,没有自己的instance variable,runtime system可以用它来生成instance。

除此之外compiler还会为每个类生成一个metaclass object,它使用来描述class object的,但是它只在内部被runtime system使用,用户是用不到它的。

Class Object的重要的作用就是生成instance,例如如下的代码

id myRect;
myRect = [Rectangle alloc];
这里Rectangle 是一个Class Object ,传给它一个alloc message然后返回的指针就赋给myRect了。便生成了一个instance myRect.

Class Method 和 instance Method

Objective -C 中,编译器将Class 代码编译生成一个Class Object这个,它不能使用instance method, 但它也有自己的继承,就是它可以从父类那里继承来一些class method, 比如version.

举一个例子,
int versionNumber = [Rectangle version];

除了这样你还可以这样
id aClass = [anObject class];
id rectClass = [Rectangle class];

因为Class Object和其他任何object一样都能被定义成id类型,因为他们都是Object,同样的任何Class Object都是属于Class这个类,所以我们同样可以type成Class.
这是两种获知id指针的方式,即可以向一个instance 传一个class消息,也可以向一个class object也就是用Rectangle来代表的这个传一个message消息,都能获知id

Class aClass = [anObject class];
Class rectClass = [Rectangle class];

Static typing

Objective-C中如果申明了类,然后定制这个对象的话,叫做static typing. 与其相对的是用id来动态的声明一个对象,将来在使用的时候可以将它弄成任意的类。

static typing的好处很多,但是不能取代dynamic binding 和 dynamic determination of a receiver's class.

任何一种对象,都可以用它所属类的父类来声明,编译的时候没所谓,runtime的时候会找出来它真正属于的类。NSObject中间还有两个方法用于揭示一个对象究竟属于那个类,他们分别是isMemberOfClass 和 isKindOfClass,这两个方法传入的参数都是someClass ,返回值是一个bool值,其中isMemberOfClass 是用以判断这个Object是不是后面那个传入的类的实例,另外一个方法isKindOfClass是用于判断这个Object 是不是后面那个传入类的实例又或者是是它父类的实例。总之是要判断这个Object是否在后面传入这个类的继承树上。

另外Objective-C中的abstract Class 与其他语言的中的规定有所不同,它没有专门标注这种抽象类的说明符号,也并不禁止从抽象类生成实例,但是因为抽象类中的很多方法并没有完成,所以即便是生成了抽象类的实例大多数情况下也没什么用处,不过也有例外,就是NSView这个类,经常被直接的使用,而另外一个经典的例子就是NSObject如果你生成一个这样的实例将什么也做不了。

2009年2月9日星期一

MVC概念以及iphone开发中的属性

Model : 用于保存程序数据的类
View : 所有的用户能看到的诸如窗体之类,还有用户能用来操作的控件
Controller: 程序逻辑,用于连接 model和view的部分,知道如何处理用户的输入,如何来响应事件.

Controller 里面的类可以通过一个特殊的变量实例outlet来引用nib里的对象,这个实例就如同一个指针指向了nib中的对象. Controller里面要操作一个nib里的对象的时候可以先声明一个outlet然后将这个outlet指向想要操作的对象,然后通过这个outlet就可以操作指向的对象了.

从操作的另外一个方向来讲,也可以在nib文件中创建接口对象,用以激发在controller中的特别处理,这类处理就称之为动作action,加入当手指离开一个按钮的时候,这本身就是一个事件,这个事件发生的时候你的controller里面的一个method就可能被触发.

(IBAction)dosomething:(id)sender 这是action方法的声明方式,注意,后面的sender是可选的,即便你声明的时候有这个sender,使用的时候也完全可以不带.

在定义一个property的时候,在"property"关键字后面带的括号中的可选属性,如果property操作的对像是一个raw type(基本类型) 比如int则不需要带retain关键字,如果操作的对像是用户构建的其他的非简单类型,则必须要带这个attribute不然就有可能在你还需要操作这个对象的时候它就已经被从内存中清除了.
到这一步一个属性的开头应该是 @property (retain,) 当然后面还有别的东西,慢慢讲. 另外一个attribute是nonatomic,其实为了实现多线程的应用,还可以选别的,但是那样会增加不必要的开销,尽管开销不大,还是应该避免,大多数情况下,这里要用nonatomic,后面其实还会看到不用nonatomic的情况.

2009年2月1日星期日

IGMP v1 notes

IGMP报文长度8byte,附带之前的IP头20byte总共是个8字节长.

一台主机上只要还有包含至少一个进程的多播组,就需要对多播路由器下发的查询报文发送报告报文.

如果一台主机上已经没有了多播组,则下次多播路由器下发查询报文的时候就不发报告报文就可以了.

多播路由器搜集了所有接口上的所有主机发送过来的报告报文,就知道自己下辖的主机们当前还在加入着哪些多播组,这样多播路由器便可以向它的上级进行报告了.

主机在报告的时候对每一个至少还含有一个进程的多播组都要发送一个报告,报告报文中IGMP后四个Byte正是这个多播组的地址, 但对于查询报文,这里是全零,也就是32个零.

重要)
主机的报告报文中的IP头部,源IP地址就是主机地址,目的IP地址是所报告的组地址,在多播路由器下发的查询报文的IP头部中,源IP地址是多播路由地址,目的地址是个224.0.0.1这个地址是保留多播地址,意思是发往子网内所有的系统组. (224.0.0.2)是发往所有的多播路由器

规则)
当第一个进程加入一个多播组的时候,主机要发送一个报告,这个报告并不能保证一定会被多播路由器收到,所以为了保险期间,过一定时间之后主机会重新发送一个报告,这个时间一般在0-10秒之间.

当多播路由器发起一个查询的时候,因为子网内可能有很多主机都加入同一个多播组,这个时候主机收到了查询报文之后并不急于发送报告报文,而是会后延一个时延,当子网中有一个主机发送应查询的要求发送了一个报告报文之后,因为目的地址是这个要报告的多播组的组地址,那么这个报告报文便可以被子网内所有同在这个多播组的其他主机收到,其他主机看到有人已经报告了这个多播组的存在,便可以不再发送关于这个多播组的报告报文了. 这样便减少了不要要的开销

在一个没有多播路由器的子网中,因为没有人发起查询,自然就不会有应查询而发出的报告报文,但是子网中的主机因为支持多播,所以当有进程在主机上加入了一个新的多播组的时候,主机还是会发出报告报文的,而这便是在没有多播路由器的子网中唯一的IGMP报文流量.

特别的多播组)
224.0.0.1是所有子网内支持多播的主机和路由都要自动加入的一个组,当接口初始化之后便自动加入这个组,这就是为什么抓包能够看到有些设备在启动的时候会自动加入到224.0.0.1这个组的原因,加入的方式是发送一个报告报文,宣称自己加入了这个组,所有在子网内的其他设备支持多播的设备都能收到这个报文,因为这个报文的目的地址是大家都加入的组224.0.0.1

TTL 一般多播报文的TTL 设定为1,这样可以控制多播报文在子网内传递,而不能被多播路由器转发.
有一个特别需要注意的地方就是对于特殊地址范围 224.0.0.0-224.0.0.255 被设定用于TTL 不大于1的应用,所以任何情况下多播路由器不会转发多播地址在这个范围内的多播报文.

2009年1月28日星期三

Meet some friends during spring festival

I took some video clips by using my iphone when met some friends during spring festival in Xi'an.

This is my first attempt to upload video onto blogger and wish me good luck~

2009年1月13日星期二

The daily use Software on MacOS X

vncviewer: Chicken of the VNC
network analyser: Wireshark for mac
HD content player: Quick time
RMVB player: RealPlayerGold 11 for mac

There are also some other Applications have their mac editions.
Such as firefox, microsoft office, msn messenger, tencent QQ, Transmission for mac, Skype for mac

By default one can delete an installed mac software by simply delete the icon from the Application Directory directly and remov its icon from the DOCK. This is a coolest feature as my opinion.

2009年1月8日星期四

工厂模式

工厂模式定义:提供创建对象的接口.

为何使用?
工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。

为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

我们以类Sample为例, 如果我们要创建Sample的实例对象:

Sample sample=new Sample();

可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。

首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成:

Sample sample=new Sample(参数);

但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重整)。

为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派切割每段,将每段再封装起来(减少段和段之间偶合联系性)这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。

在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。

这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample HisSample .我们要实例化他们时,如下:

Sample mysample=new MySample();
Sample hissample=new HisSample();

随着项目的深入,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.

但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了.

工厂方法
你会建立一个专门生产Sample实例的工厂:

public class Factory{

  public static Sample creator(int which){

  //getClass 产生Sample 一般可使用动态类装载装入类。
  if (which==1)
    return new SampleA();
  else if (which==2)
    return new SampleB();

  }

}

那么在你的程序中,如果要实例化Sample.就使用

Sample sampleA=Factory.creator(1);

这样,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.

使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有Sample接口的实现类,SampleA,其次要有一个factory类,用来生成产品Sample,如下图,最右边是生产的对象Sample

进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory

抽象工厂
工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory).

这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2.

这里假设:Sample有两个concreteSampleASamleB,而Sample2也有两个concreteSample2ASampleB2

那么,我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现,下面就是将上例中的Factory拓展成抽象工厂:

public abstract class Factory{

  public abstract Sample creator();

  public abstract Sample2 creator(String name);

}

public class SimpleFactory extends Factory{

  public Sample creator(){
    .........
    return new SampleA
  }

  public Sample2 creator(String name){
    .........
    return new Sample2A
  }

}

public class BombFactory extends Factory{

  public Sample creator(){
    ......
    return new SampleB
  }

  public Sample2 creator(String name){
    ......
    return new Sample2B
  }

}

从上面看到两个工厂各自生产出一套SampleSample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产SampleSample2?

抽象工厂还有另外一个关键要点,是因为 SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory

在实际应用中,工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用,

举例

我们以JiveForumFactory为例,这个例子在前面的Singleton模式中我们讨论过,现在再讨论其工厂模式:

public abstract class ForumFactory {

  private static Object initLock = new Object();
  private static String className = "com.jivesoftware.forum.database.DbForumFactory";
  private static ForumFactory factory = null;

  public static ForumFactory getInstance(Authorization authorization) {
    //If no valid authorization passed in, return null.
    if (authorization == null) {
      return null;
    }
    //以下使用了Singleton 单态模式
    if (factory == null) {
      synchronized(initLock) {
        if (factory == null) {
            ......

          try {
              //动态转载类
              Class c = Class.forName(className);
              factory = (ForumFactory)c.newInstance();
          }
          catch (Exception e) {
              return null;
          }
        }
      }
    }

    //Now, 返回 proxy.用来限制授权对forum的访问
    return new ForumFactoryProxy(authorization, factory,
                    factory.getPermissions(authorization));
  }

  //真正创建forum的方法由继承forumfactory子类去完成.
  public abstract Forum createForum(String name, String description)
  throws UnauthorizedException, ForumAlreadyExistsException;

  ....

}

因为现在的Jive是通过数据库系统存放论坛帖子等内容数据,果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口:

private static String className = "com.jivesoftware.forum.database.DbForumFactory";

你可以使用自己开发的创建forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以.

在上面的一段代码中一共用了三种模式,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果.

看看Java宠物店中的CatalogDAOFactory:

public class CatalogDAOFactory {

  /**

  * 本方法制定一个特别的子类来实现DAO模式。
  * 具体子类定义是在J2EE的部署描述器中。
  */

  public static CatalogDAO getDAO() throws CatalogDAOSysException {

    CatalogDAO catDao = null;

    try {

      InitialContext ic = new InitialContext();
      //动态装入CATALOG_DAO_CLASS
      //可以定义自己的CATALOG_DAO_CLASS从而在无需变更太多代码
      //的前提下,完成系统的巨大变更。

      String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS);

      catDao = (CatalogDAO) Class.forName(className).newInstance();

    } catch (NamingException ne) {

      throw new CatalogDAOSysException("
        CatalogDAOFactory.getDAO: NamingException while
          getting DAO type : \n" + ne.getMessage());

    } catch (Exception se) {

      throw new CatalogDAOSysException("
        CatalogDAOFactory.getDAO: Exception while getting
          DAO type : \n" + se.getMessage());

    }

    return catDao;

  }

}

CatalogDAOFactory是典型的工厂方法,catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类,这个实现子类在Java宠物店是用来操作catalog数据库,用户可以根据数据库的类型不同,定制自己的具体实现子类,将自己的子类名给与CATALOG_DAO_CLASS变量就可以。

由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。

设计模式如何在具体项目中应用见《Java实用系统开发指南》

今天开张了

简单记叙,

Objective-C 里面的Class

对于每一个Class编译器只创建一个可访问的Object,这个class object 知道如何创建属于这个类的新的对象,正因为这样的原因,这个对象被称之为factory object.

由这个对象创建的都是这个Class的实例,真正在程序中起作用的对象都是这个factory object在运行时创建的实例。每个实例对象都有各自独立的instance variables,但是他们的method都是共享的。

在命名的时候,习惯上将Class名的首字母大写,将实例对象名字的首字母小写。
比如,一个Class 名字可能是 Rectangle 一个相应的实例的名字可能是 myRect
由上可见,实例命名的时候第一个单词的首字母小写,之后的每个单词的首字母都应该大写