面试题总结(一)

2017-07-21  本文已影响11人  冰land

(1)isa指针问题

isa:是一个Class类型的指针。
每个实例对象有一个isa的指针,它指向对象的类,而Class里也有一个isa的指针,指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向它父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向本身,这样形成了一个封闭的内循环。

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(元类),类也是对象,是元类的实例。

整理一下清晰的思路是有必要的:

大致逻辑如下:
实例对象--(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:方法的底层实现机制如下:

(5)结构体当中能定义oc对象吗? instancetype

不能,因为结构体当中只能是类型的声明不能进行分配空间.

(6)id类型是什么,instancetype是什么,有什么区别?

上一篇 下一篇

猜你喜欢

热点阅读