id类型
本小节知识:
- 【理解】静态类型和动态类型
- 【理解】为什么要有动态类型?
- 【理解】id数据类型与静态类型
1.静态类型和动态类型
- 静态类型
- 将一个指针变量定义为特定类的对象时,使用的是静态类型,在编译的时候就知道这个指针变量所属的类,这个变量总是存储特定类的对象。
Person *p = [Person new];
- 动态类型
- 这一特性是程序直到执行时才确定对象所属的类
id obj = [Person new];
2.为什么要有动态类型?
- 我们知道NSObject是OC中的基类
- 那么任何对象的NSObject类型的指针可以指向任意对象,都没有问题
- 但是NSObject是静态类型,如果通过它直接调用NSObject上面不存在的方法,编译器会报错。
- 你如果想通过NSObject的指针调用特定对象的方法,就必须把NSObject * 这种类型强转成特定类型。然后调用。如下
//定义NSObject * 类型
NSObject* obj = [Cat new];
Cat *c = (Cat*)obj;
[c eat];
- id 是一种通用的对象类型,它可以指向属于任何类的对象,也可以理解为万能指针 ,相当于C语言的 void *
- 因为id是动态类型,所以可以通过id类型直接调用指向对象中的方法, 编译器不会报错
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
id obj = [C at new];
[obj eat]; // 不用强制类型转换
[obj test]; //可以调用私有方法
- 注意:
- 在id的定义中,已经包好了*号。id指针只能指向OC中的对象
- 为了尽可能的减少编程中出错,Xcode做了一个检查,当使用id 类型的调用本项目中所有类中都没有的方法,编译器会报错
- id类型不能使用.语法, 因为.语法是编译器特性, 而id是运行时特性
3.id数据类型与静态类型
-
虽然说id数据类型可以存储任何类型的对象,但是不要养成滥用这种通用类型
- 如没有使用到多态尽量使用静态类型
- 静态类型可以更早的发现错误(在编译阶段而不是运行阶段)
- 静态类型能够提高程序的可读性
- 使用动态类型前最好判断其真实类型
-
动态类型判断类型
- - (BOOL)isKindOfClass:classObj 判断实例对象是否是这个类或者这个类的子类的实例
Person *p = [Person new];
Student *stu = [Student new];
BOOL res = [p isKindOfClass:[Person class]];
NSLog(@"res = %i", res); // YES
res = [stu isKindOfClass:[Person class]];
NSLog(@"res = %i", res); // YES
- (BOOL) isMemberOfClass: classObj 判断是否是这个类的实例
Person *p = [Person new];
Student *stu = [Student new];
BOOL res = [p isMemberOfClass:[Person class]];
NSLog(@"res = %i", res); // YES
res = [stu isMemberOfClass:[Person class]];
NSLog(@"res = %i", res); // NO
+ (BOOL) isSubclassOfClass:classObj 判断类是否是指定类的子类
BOOL res = [Person isSubclassOfClass:[Student class]];
NSLog(@"res = %i", res); // NO
res = [Student isSubclassOfClass:[Person class]];
NSLog(@"res = %i", res); // YES
SEL类型
本小节知识:
- 【理解】什么是SEL类型
- 【掌握】SEL使用
- 【理解】OC方法查找顺序
1.什么是SEL类型
-
SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系
-
每个类的方法列表都存储在类对象中
-
每个方法都有一个与之对应的SEL类型的对象
-
根据一个SEL对象就可以找到方法的地址,进而调用方法
-
SEL类型的定义
- typedef struct objc_selector *SEL;
-
首先把test这个方法名包装成sel类型的数据
-
根据SEL数据到该类的类对象中,去找对应的方法的代码,如果找到了就执行该代码
-
如果没有找到根据类对象上的父类的类对象指针,去父类的类对象中查找,如果找到了,则执行父类的代码
-
如果没有找到,一直像上找,直到基类(NSObject)
-
如果都没有找到就报错。
-
注意: + 在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。
Dog *dog=[[Dog alloc] init];
[dog eat];
2.SEL使用
-
定义普通的变量
- 如:SEL sel = @selector(show);
-
作为方法实参与NSObject配合使用
-
检验对象是否实现了某个方法
- - (BOOL) respondsToSelector: (SEL)selector 判断实例是否实现这样方法
- + (BOOL)instancesRespondToSelector:(SEL)aSelector;
BOOL flag;
// [类 respondsToSelector]用于判断是否包含某个类方法
flag = [Person respondsToSelector:@selector(objectFun)]; //NO
flag = [Person respondsToSelector:@selector(classFun)]; //YES
Person *obj = [[Person alloc] init];
// [对象 respondsToSelector]用于判断是否包含某个对象方法
flag = [obj respondsToSelector:@selector(objectFun)]; //YES
flag = [obj respondsToSelector:@selector(classFun)]; //NO
// [类名 instancesRespondToSelector]用于判断是否包含某个对象方法
// instancesRespondToSelectorr只能写在类名后面, 等价于 [对象 respondsToSelector]
flag = [Person instancesRespondToSelector:@selector(objectFun)]; //YES
flag = [Person instancesRespondToSelector:@selector(classFun)]; //NO
- 让对象执行某个方法
- - (id)performSelector:(SEL)aSelector;
- - (id)performSelector:(SEL)aSelector withObject:(id)object;
- - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
Person *p = [Person new];
SEL s1 = @selector(objectFun);
[p performSelector:s1];
SEL s2 = @selector(objectFun:);
[p performSelector:s2 withObject:@"lnj"];
SEL s3 = @selector(objectFun:value2:);
[p performSelector:s3 withObject:@"lnj" withObject:@"lmj"];
SEL s4 = @selector(classFun);
[Person performSelector:s4];
SEL s5 = @selector(classFun:);
[Person performSelector:s5 withObject:@"lnj"];
SEL s6 = @selector(classFun:value2:);
[Person performSelector:s6 withObject:@"lnj" withObject:@"lmj"];
- 作为方法形参
@implementation Person
- (void)makeObject:(id) obj performSelector:(SEL) selector
{
[obj performSelector:selector];
}
@end
int main(int argc, const char * argv[]) {
Person *p = [Person new];
SEL s1 = @selector(eat);
Dog *d = [Dog new];
[p makeObject:d performSelector:s1];
return 0;
}
3.OC方法查找顺序
- 1.给实例对象发送消息的过程(调用对象方法) + 根据对象的isA指针去该对象的类方法中查找,如果找到了就执行 + 如果没有找到,就去该类的父类类对象中查找 + 如果没有找到就一直往上找,直到跟类(NSObject) + 如果都没有找到就报错
- 2.给类对象发送消息(调用类方法)
- 根据类对象的isA指针去元对象中查找,如果找到了就执行
- 如果没有找到就去父元对象中查找
- 如果如果没有找到就一直往上查找,直到根类(NSOject)
- 如果都没有找到就报错