(1)isa指针问题
isa:是一个Class类型的指针。
每个实例对象有一个isa的指针,它指向对象的类,而Class里也有一个isa的指针,指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向它父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向本身,这样形成了一个封闭的内循环。
- id类型是一个objc_objec结构体的指针。
- objc_object结构体包含一个Class类型的变量isa。
- Class是objc_class结构体的指针。
总结:类是用objc_class结构体表示的,对象是用objc_object结构体表示的。
objc_class结构体的定义如下:
struct objc_class {
Class isa //所属类的指针
Class super_class//指向父类的指针
const char *name //类名
long version // 版本
long info //供运行期使用的一些位标识。
long instance_size //实例大小
struct objc_ivar_list *ivars //成员变量数组
struct objc_method_list **methodLists //方法列表
struct objc_cache *cache//指向最近使用的方法.用于方法调用的优化
struct objc_protocol_list *protocols//协议的数组
}
objc_class结构体中也有一个Class类型的isa指针,该指针指向的是meteClass(元类),类也是对象,是元类的实例。
整理一下清晰的思路是有必要的:
- 实例对象在运行时被表示成objc_object类型结构体,结构体内部有一个isa指针指向objc_class结构体。
- objc_class内部保存了类的成员变量和方法列表以及其他一些信息,并且还有一个isa指针。这个isa指针指向meteClass(元类),元类里保存了这个类的类方法列表。
- 为了完整性,其实元类里也有一个isa指针,这个isa指针,指向的是根元类,根元类的isa指针指向自己
大致逻辑如下:
实例对象--(runtime)-->objc_object--(isa)-->objc_class--(isa)-->元类--(isa)-->(根元类)-->自己
(2)怎么使用performSelector传入3个以上的参数,其中一个参数为结构体?
点开NSObject头文件我们可以看到提供了三个performSelector方法:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
可以看出系统提供的API中,参数最多的才两个参数,而且参数类型还是id类型,而像结构体本身就不是对象,作为参数该怎么实现呢?
没有办法,我们只能通过对象的方法来实现,代码如下:
#import <Foundation/Foundation.h>
typedef struct YJStruct {
int a;
int b;
}*my_struct;
@interface YJObject : NSObject
@property (nonatomic, assign) my_struct arg3;
@property (nonatomic, copy) NSString *arg1;
@property (nonatomic, copy) NSString *arg2;
@end
#import "YJObject.h"
@implementation YJObject
//在堆上分配的内存,我们要手动释放
- (void)dealloc {
free(self.arg3);
}
@end
测试:
- (void)viewDidLoad {
[super viewDidLoad];
my_struct str = (my_struct)(malloc(sizeof(my_struct)));
str ->a = 1;
str ->b = 2;
YJObject *obj = [[YJObject alloc]init];
obj.arg1 = @"arg1";
obj.arg2 = @"arg2";
obj.arg3 = str;
[self performSelector:@selector(call:) withObject:obj];
}
- (void)call:(YJObject *)obj
{
NSLog(@"a:%d --- b:%d",obj.arg3 ->a,obj.arg3 ->b);
}
打印结果: a:1 --- b:2
(3)下面的代码输出的是什么?
@implementation Son
-(instancetype)init {
self = [super init];
if (self) {
NSLog(@"%@",NSStringFromClass([self class]));
NSLog(@"%@",NSStringFromClass([super class]));
}
return self;
}
@end
打印结果:
Son
Son
结果跟你想的一样么?不管相同与否,让我们了解一样为什么是这样的结果.
详解
本题目主要考察的是Objective-C中对self和super的理解.我们都知道:self是类的隐藏参数,指向当前调用方法的这个类的实例.那么super呢?
很多人想当然的认为"super"和self类似,应该是指向父类的指针吧!!!这是很普遍的一个误区~~其实super是一个Magic Keyword,它的本质是一个编译器标示符,和self是指向的同一个消息接受者!他们两个的不同点在于:super会告诉编译器,调用class这个方法时,要去父类中去调用,而不是在本类中.
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前Son * xxx这个对象.
当使用self调用方法时,会在当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用super时,则从父类的方法列表中开始找.然后调用父类的这个方法.
(4)若一个类有实例变量NSString *_foo ,调用setValue:forKey:时, 可以以foo还是_foo作为key?
详解
两者都可以
顺便带一嘴反编译
首先 cd 工程目录下, 然后 clang -rewrite-objc main.m
@interface YJObject : NSObject
@property (nonatomic, copy) NSString * name;
@end
setValue:forKey:方法的底层实现机制如下:
- 程序优先调用setName:方法,代码通过setter方法完成设置.
- 如果没有找到setName:方法,KVC机制会检查+(BOOL)accessInstanceVariableDirectly方法有没有返回YES,默认该方法返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUndefinedKey:方法,不过一般开发者不会这么做. 所以KVC机制会搜索该类里面有没有名为_name的成员变量, 无论该变量是在类接口部分定义, 还是在类实现部分定义, 也无论用了什么样的访问修饰符,只要存在以_name命名的变量,KVC都可以对该成员变量赋值
- 如果该类既没有setName:方法,也没有_name成员变量,KVC机制会搜索_isName的成员变量
- 和上面一样,如果该类 既没有setName:方法, 也没有_name和_isName成员变量, KVC机制会继续搜索name和isName的成员变量, 然后再给他们赋值.
- 如果上面列出的方法或者成员变量都不存在, 系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常....
(5)结构体当中能定义oc对象吗? instancetype
不能,因为结构体当中只能是类型的声明不能进行分配空间.
(6)id类型是什么,instancetype是什么,有什么区别?
- id类型:万能指针,能作为参数、方法的返回值类型
- instancetype:只能作为方法的范围类型,并且返回的类型是当前定义类的类类型
区别1:
在ARC环境下:
instancetype用来在编译期确定实例的类型,而使用id的话,编译器不检查类型,运行时检查类型。
在MRC环境下:
instancetype和id一样,不做具体类型检查
区别2:
id可以作为方法的参数,但instancetype不可以
instancetype只适用于初始化方法和便利构造器的返回值类型
网友评论