实体化后的类我们称之为对象(Object)
在Objective-C里面,类的 定义从@interface开始到@end结束,也就是说,编译器看到了@interface就知道了这是类的定义的开始,看到了@end就知道,类的定 义结束了。
在Obejctive-C里面原则上,你可以不使用 NSObject,构筑一个你自己的根类,但是事实上这样做将会有很大工作量,而且这样做没有什么意义,因为苹果为你提供的NSObject经过了很长时 间的检验。
(-)减号就是告诉编译器,减号后面的方法,是实体方法(instance method)。实体方法的意思就是说,这个方法在类没有被实体化之前,是不能运行的。(+)我们把带加号的 方法称为类方法(class method),和实体方法相对应,类方法可以脱离实体而运行。
在Objective-C里面方法的返回类型需要用圆括号包住,当编译器看到减号或者加号后面的括号了之后,就会认为这是在声明方法的返回值。你也 可以不声明返回值,Objective-C的编译器会给没有写显式的返回值函数加上一个默认的返回值,它的类型是id。
编译器会把从@implementation到@end之间的部分看作是类的定义。@implementation的后面要有一个空格。
我们来回忆一下C语言里面函数的调用过程,实际上编译器在编译的时候就已经把函数相对于整个执行包的入口地址给确定好了,函数的执行实际上就是直接 从这个地址开始执行的。Objective-C使用的是一种间接的方式, Objective-C向对象或者类(具体上是对象还是类的名字取决于方法是实体方法还是类方法)发送消息,消息的格式应该和方法相同。[Cattlenew]就是说,向Cattle类发送一个new的消息。这样当Cattle类接收到new的时候,就会查找它可以相应的消息的列表,找到了new之后就会调用new的这个类方法,分配内存和初始化完成之后返回一个id,这样我们就得到一个对象。
Objective-C在编译的过程当中,编译器是会去检查方法是否有效的,如果无效会给你一个警告。但是编译器并不会阻止你执行,因为只有在执行 的时候才会触发消息,编译器是无法预测到执行的时候会发生什么奇妙的事情的。使用这样的机制给程序毫无疑问将给带来极大的灵活性,因为我们和任意的对对象 或者类发送消息,只要我们可以保证执行的时候类可以准确地找到消息并且执行就可以了,当然如果找不到的话,运行会出错。
%@,这是在告诉编译器,需要把%@用一个后面定义的字符串或者其他什么的来替换。
SEL类型
Objective-C在编译的时候,会根据方法的名字(包括参数序列),生成一个用 来区分这个方法的唯一的一个ID,这个ID就是SEL类型的。我们需要注意的是,只要方法的名字(包括参数序列)相同,那么它们的ID都是相同的。就是 说,不管是超类还是子类,不管是有没有超类和子类的关系,只要名字相同那么ID就是一样的。
1.SEL变量名=@selector(方法名字);
2.SEL变量名=NSSelectorFromString(方法名字的字符串);
3.NSString*变量名=NSStringFromSelector(SEL参数);
其中第1行是直接在程序里面写上方法的名字,第2行是写上方法名字的字符串,第3行是通过SEL变量获得方法的名字。我们得到了SEL变量之后,可以通过下面的调用来给一个对象发送消息:
这样的机制大大的增加了我们的程序的灵活性,我们可以通过给一个方法传递SEL参数,让这个方法动态的执行某一个方法;我们也可以通过配置文件指定需要执行的方法,程序读取配置文件之后把方法的字符串翻译成为SEL变量然后给相应的对象发送这个消息。
从效率的角度上来说,执行的时候不是通过方法名字而是方法ID也就是一个整数来查找方法,由于整数的查找和匹配比字符串要快得多,所以这样可以在某种程度上提高执行的效率。
这里我讲一下selector多参数的问题
@interfaceNSObject(MoreSelctorParam)
-(id)performSelector:(SEL)aSelectorwithObjects:(NSArray*)objects;
@end
@implementationNSObject(MoreSelctorParam)
-(id)performSelector:(SEL)selectorwithObjects:(NSArray*)objects
{
NSMethodSignature*signature=[selfmethodSignatureForSelector:selector];//方法签名
if(signature)//如果签名成功
{
NSInvocation*invocation=[NSInvocationinvocationWithMethodSignature:signature];//调用
[invocationsetTarget:self];
[invocationsetSelector:selector];//调用谁呢?selector啊!
for(inti=0;i<[objectscount];i++)
{
idobject=[objectsobjectAtIndex:i];
[invocationsetArgument:&objectatIndex:(i+2)];//为什么在2因为0和1被target和selector占用了。。
}
[invocationretainArguments];//防止被释放
[invocationinvoke];//调用
//有返回值
if(signature.methodReturnLength)
{
idanObject;
[invocationgetReturnValue:&anObject];
returnanObject;
}else
{
returnnil;
}
}
else//不成功
{
returnnil;
}
}
@end
问题就出现了不知道你们注意没有?
-(void)printInt:(int)iString:(NSString*)s;
-(void)printInt:(NSInteger)iString:(NSString*)s;
-(void)printInt:(id)iString:(NSString*)s;
[objperformSelector:@selector(printInt:String:)withObjects:[NSArrayarrayWithObjects:[[NSNumberalloc]initWithInt:5],@"thisstring",nil]];
前2个结果为i->151608432,s->thisstring
后一个结果为i->5,s->thisstring
所以你就把参数类型写成id吧!原因啊,我也不知道!
对于Objective-C里面的类的实例变量而言,在编译器的范围里面,是有作用域的。和其他的语言一样,Objective-C也支持public,private还有protected作用域限定。
如果一个实例变量没有任何的作用域限定的话,那么缺省就是protected。
如果一个实例变量适用于public作用域限定,那么这个实例变量对于这个类的派生类,还有类外的访问都是允许的。
如果一个实例变量适用于private作用域限定,那么仅仅在这个类里面才可以访问这个变量。
如果一个实例变量适用于protected作用域限定,那么在这个类里面和这个类的派生类里面可以访问这个变量,在类外的访问是不推荐的。
前置加号(+)的方法为类方法,这类方法是可以直接用类名来调用的,它的作用主要是创建一个实例。 前置减号(-)的方法为实例方法,必须使用类的实例才可以调用的。
和其他的语言类似,下面是类方法的一些规则,请大家务必记住。
1,类方法可以调用类方法。
2,类方法不可以调用实例方法,但是类方法可以通过创建对象来访问实例方法。
3,类方法不可以使用实例变量。类方法可以使用self,因为self不是实例变量。
4,类方法作为消息,可以被发送到类或者对象里面去(实际上,就是可以通过类或者对象调用类方法的意思)。