问题整理

1、代理的效率高,还是KVO的效率高?

代理的效率高,因为代理不会动态的生成类

2、怎么拿到一个对象的类对象,怎么拿到一个类的元类对象

isa指针指向的就是类对象
object_getClass(self.per1) == self.per1.isa

 NSLog(@"类对象  %@  %@",
          object_getClass(self.per1),
          object_getClass(self.per2));

object_getClass(object_getClass(self.per1)) == self.per1.isa.isa

 NSLog(@"元类对象  %@  %@",
          object_getClass(object_getClass(self.per1)),
          object_getClass(object_getClass(self.per2)));

3.1、添加KVO的对象的元类和不添加KVO的对象的元类是否是一个?

不是同一个,添加KVO的元类是 NSKVONotifying_类名

image.png

3.2、iOS用什么方式实现一个对象的KVO?(KVO的本质是什么)

比如给Person对象的age属性添加KVO监听

1、利用Runtime生成Person子类<NSKVONotifying_Person>,并且让对象的isa指针指向NSKVONotifying_Person
2、当修改Personage属性时候,会调用Foundation_NSSet***ValueAndNotify函数
2.1、调用willChangeValueForKey
2.2、调用super的属性setAge:方法
2.3、调用didChangeValueForKey
2.3.1、内部调用- (void)observeValueForKeyPath: ofObject:change: context:;

3.3、怎么手动触发KVO监听?

调用对象的

[self.per1 willChangeValueForKey:@"age"];
[self.per1 didChangeValueForKey:@"age"];
image.png

3.4、直接修改成员变量会触发KVO吗?

@interface Person : NSObject
{
    @public
    NSString *_name;
}

/// 名字
@property (nonatomic, copy) NSString * name;
@end
self.per->_name = @"333";

不能,必须要触发set方法,或者【手动触发】KVO才可以,详情见上面。

怎么才能实现,直接修改成员变量,触发KVO呢?

[self.per willChangeValueForKey:@"name"];
self.per->_name = @"333";
[self.per didChangeValueForKey:@"name"];

4.1、KVC setValue: forKeyPath:的实现原理

未命名文件.png

4.2、KVC操作能不能触发KVO的监听

@interface Person : NSObject
{
    @public
    int _age;
}

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    Person *per = [[Person alloc] init];
    
    [per addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    
    [per setValue:@10 forKeyPath:@"age"];
    
    [per removeObserver:self forKeyPath:@"age"];
    
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"keyPath = %@   %@",keyPath,change);
}

可以触发<满足KVC的任何一种方式都可以触发>,可能调用KVC的时候内部触发了

[per willChangeValueForKey:@"age"];
per->_age = 123;
[per didChangeValueForKey:@"age"];
触发KVO

4.3、KVC valueForKey:的原理

KVC valueForKey: 的原理.png

4.4、怎么给一个属性赋值,并且不触发set方法

@interface Person : NSObject

/// name
@property (nonatomic, copy) NSString * name;

@end

- (void)setName:(NSString *)name {
    _name = [name copy];
    
    NSLog(@"触发了set方法   name === %@",_name);
}

 Person *per = [[Person alloc] init];
[per setValue:@"123" forKey:@"_name"];
    
NSLog(@"per.name === %@",per.name);
    

5、一个NSObject对象占用多少内存

NSObject对象中就一个isa指针,一个指针占用内存的大小(64bit环境下8个字节,32bit环境下4个字节)

5.1、对于一个自定义的对象,占用多大的空间呢?
@interface Person : NSObject
{
    @public
    int _age;
    int _height;
}
@end
struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    int _height;
};

16字节 = isa(8) + int(4) +int(4)

5.2、对于一个如下自定义的对象,占用多大的空间呢?

在64bit的环境下,一个Person对象和一个Student对象占用多少内存空间?

// Person
@interface Person : NSObject
{
    @public
    int _age;
}
@end

@implementation Person
@end

// Student
@interface Student : Person
{
    @public
    int _height;
}
@end

@implementation Student
@end



分析.png

代码分析:

struct NSObject_IMPL {
    Class isa;  // 8个字节
};

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;    // 8个字节
    int _age;   // 4个字节
};  // 16个字节<内存对齐>

struct Student_IMPL {
    struct Person_IMPL Person_IVARS;    // 16个字节,空余四个
    int _height;    // 4个字节
};  // 16个字节<内存对齐>

6、对象的isa指针指向哪?

instance的isa指向class
class的isa指向meta-class
meta-class的isa指向基类meta-class
底层原理总结 — isa和superclass

7、OC的类信息都放在哪里?

成员变量的具体值存放在instance对象中
对象方法,协议,属性,成员变量信息放在class对象中
类方法信息存放在meta-class对象中

8、判断class是否为meta-class

Class metaClass = object_getClass([Person class]);
Class class = [Person superclass];
NSLog(@"%d  %d",class_isMetaClass(metaClass),class_isMetaClass(class));

结果:

1  0
9.1、Category的原理是啥?

Category编译之后的底层结构是struct category_t,里面存储着分类的对象方法,类方法,协议,属性信息。
在程序运行的时候,runtime会将Category的数据合并到类中<类对象和元类对象>

9.2、当我们调用Category的方法是怎么调用的?

参考第一条问题答案。
调用对象方法:通过对象的isa找到类对象,找到类对象中的方法,调用
调用类方法:通过类对象的isa找到元类对象,扎到元类对象中的类方法,调用

9.3、当Category中和类中存在相同方法的时候,最终调用的是哪一个?

调用的是Category中的方法
因为类方法列表已经放到整体方法列表的后面了。
所以调用相同方法的时候,会优先遍历到分类中的方法

image.png
9.4、当多个Category中存在相同方法的时候,最终调用的是哪一个?

会调用后编译的分类中的方法
因为runtime将分类中的方法列表合并到类对象中的时候,是倒叙遍历的,所以后被编译的分类中的方法列表优先被加到类对象中。


image.png
image.png

那么分类的编译顺序在哪里查看呢?


image.png
9.5、CategoryClass Extension有啥区别?
#import "Person.h"

@interface Person()
@property (nonatomic, copy) NSString *address;
@end

@implementation Person
- (void)run {
    NSLog(@"Person");
}
@end

Class Extension在编译的时候,他的数据已经包含在了Class中了
Category中的信息要等到runtime的时候才会合并到Class

你可能感兴趣的:(问题整理)