OC语法相关

2020-08-07  本文已影响0人  KeepOnline

1.一个NSObject对象占用多少内存?

2.对象的isa指针指向哪里?

3.OC的类信息存放在哪里?

KVO

未使用KVO监听的对象

使用了KVO监听的对象

_NSSet*ValueAndNotify的内部实现
    [self willChangeValueForKey:@"age"];
    // 原来的setter实现
    [self didChangeValueForKey:@"age"];

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

2.如何手动触发KVO?

手动调用willChangeValueForKey:和didChangeValueForKey:

3.直接修改成员变量会触发KVO么?

不会触发KVO

4.通过KVC修改属性会触发KVO么?

会触发KVO

KVC

常见的API

    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
    - (void)setValue:(id)value forKey:(NSString *)key;
    - (id)valueForKeyPath:(NSString *)keyPath;
    - (id)valueForKey:(NSString *)key;

setValue:forKey:的原理

valueForKey:的原理

Category

Category的底层结构

    struct category_t {
        const char * name;
        classref_t cls;
        struct method_list_t * instanceMethods;
        struct methos_list_t * classMethods;
        struct protocol_list_t * protocols;
        struct property_list_t * instanceProperties;
        struct property_list_t * _classProperties;
        
        method_list_t * methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
        
        property_list_t * propertiesForMeta(bool isMeta, struct header_info * hi);
    };

Category的加载处理过程

源码解读顺序

objc-os.mm
        _objc_init
        map_images
        map_images_nolock
        
objc_runtime-new.mm
        _read_images
        remethodizeClass
        attachCategories
        attachLists
        realloc、memmove、memcpy

1.Category的使用场合是什么?

2.Category中有load方法吗?load方法是什么时候调用的?load方法能继承吗?

3.Category能否添加成员变量?如果可以,如何给Category添加成员变量?

如何实现给分类”添加成员变量“?

    // 添加关联对象
    void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
    
    // 获得关联对象
    id objc_getAssociatedObject(id object, const void * key)
    
    // 移除所有的关联对象
    void objc_removeAssociatedObjects(id object)

key的常见用法
    static void *MyKey = &MyKey;
    objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    objc_getAssociatedObject(obj, &MyKey)
    
    static char MyKey;
    objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    objc_getAssociatedObject(obj, &MyKey)
    
    // 使用属性名作为key
    objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    objc_getAssociatedObject(obj, @"property")
    
    // 使用get方法的@selector作为key
    objc_setAssociatedObject(obj, @selected(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    objc_getAssociatedObject(obj, @selector(getter))    
    

objc_AssociationPolicy

objc_AssociationPolicy 对应的修饰符
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC strong,nonatomic
OBJC_ASSOCIATION_COPY_NONATOMIC copy,nonatomic
OBJC_ASSOCIATION_RETAIN strong, atomic
OBJC_ASSOCIATION_COPY copy, atomic

关联对象的原理

objc4源码解读:objc-references.mm

    class AssociationsManager {
        static AssociationsHashMap *_map;
    };
    
    class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap>
    class ObjectAssociationMap : public std::map<void *, ObjcAssociation>
    class ObjcAssociation {
        uintptr_t _policy;
        id _value;
    };

关联对象并不是存储在被关联对象本身内存中

关联对象存储在全局的统一的一个AssociationsManager中

设置关联对象为nil,就相当于是移除关联对象

+load方法

源码解读顺序

+load方法时根据方法地址直接调用,并不是经过objc_msgSend函数调用

objc-os.mm
        _objc_init
        load_images
        prepare_load_methods
            schedule_class_load
            add_class_to_loadable_list
            add_category_to_loadable_list
        call_load_methods
            call_class_loads
            call_category_loads
            (*load_method)(cls,SEL_load)

+initialize方法

Block

block的本质

block变量捕获(capture)

变量类型 捕获到block内部 访问方式
局部变量 auto 值传递
局部变量 static 指针访问
全局变量 直接访问

atuo变量的捕获

    
    int age = 20;
    void (^block)(void) = ^{
        NSLog(@"age is %d", age);
    };
    
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0 *Desc;
        int age;
    };
    
    // impl
    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    // Desc
    struct __main_block_desc_0 {
        size_t reserved;
        size_t Block_size;
    };

block的类型

block类型 环境
__NSGlobalBlock__ 没有访问auto变量
__NSStackBlock__ 访问了auto变量
__NSMallocBlock__ __NSStackBlock__调用了copy
Block的类 副本源的配置存储域 复制效果
_NSConcreteStackBlock 从栈复制到堆
_NSConcreteGlobalBlock 程序的数据区域 什么也不做
_NSConcreteMallocBlock 引用计数增加

block的copy

对象类型的auto变量

1.block的原理是怎样的?本质是什么?

封装了函数调用以及调用环境的OC对象

2.block的属性修饰词为什么是copy?使用block有哪些使用注意?

函数 调用时机
copy函数 栈上的Block赋值到堆时
dispose函数 堆上的Block被废弃时

__weak问题解决

__block修饰符

    __block int age = 10;
    ^{
        NSLog(@"%d", age);
    }();
    
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0 * Desc;
        __Block_byref_age_0 * age; // by ref
    };
    
    struct __Block_byref_age_0 {
        void *__isa;
        __Block_byref_age_0 * __forwarding;
        int __flags;
        int __size;
        int age;
    };
    

__block的内存管理

__block的__forwarding指针

对象类型的auto变量、__block变量

对象 BLOCK_FIELD_IS_OBJECT
__block变量 BLOCK_FIELD_IS_BYREF

循环引用问题

对象(self)持有Block,Block持有对象(self)

解决循环引用问题 - ARC

    __weak typeof(self) weakSelf = self;
    self.block = ^{
        printf("%p", weakSelf);
    };
    
    __unsafe_unretained id weakSelf = self;
    self.block = ^{
        NSLog(@"%p", weakSelf);
    };

对象(self)持有Block,Block对对象的持有是弱引用

    __block id weakSelf = self;
    self.block = ^{
        printf("%p", weakSelf);
        weakSelf = nil; // (关键:设置为nil,将闭合的环切断)
    };
    self.block();

解决循环引用问题 - MRC

    __unsafe_unretained id weakSelf = self;
    self.block = ^{
        NSLog(@"%p",weakSelf);
    };
    __block id weakSelf = self;
    self.block = ^{
        printf("%p", weakSelf);
    };

Objective-C的本质

思考:Objective-C的对象、类主要是基于C\C++的什么数据结构实现的?

OC对象的本质

思考:一个OC对象在内存中是如何布局的?
NSObject的底层实现

@interface NSObject {
    Class isa;
}
@end

struce NSObject_IMPL {
    Class isa;
};

typedef struct objc_class *Class;

NSObject *obj = [[NSObject class] init];



@interface Student : NSObject {
    @public
    int _no;
    int _age;
}
@end

struct Student_IMPL {
    Class isa;
    int _no;
    int _age;
};

Student *stu = [[Student alloc] init];
stu->_no = 4;
stu->_age = 5;

struct Student_IMPL *stu2 = (__bridge struct Student_IMPL *)stu;
NSLog(@"%d, %d", stu2->_no, stu2->age);


@interface Student : NSOjbect {
    @public
    int _no;
    int _age;
}
@end

struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
};

struct NSObject_IMPL {
    Class isa;
};


思考:一个Person对象、一个Student对象占用多少内存空间?


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

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age
};

struct NSObject_IMPL {
    Class isa;
};


@interface Student : Person {
    int _no;
}
@end

struct Student_IMPL {
    struct Person_IMPL Person_IVARS;
    int _no;
};


实时查看内存数据

2个容易混淆的函数

创建一个实例对象,至少需要多少内存


#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);

创建一个实例对象,实际上分配了多少内存?

#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);

OC对象的分类

Objective-C中的对象,简称OC对象,主要可以分为3种

instance

  • instance对象就是通过alloc出来的对象,每次调用alloc都会产生新的instance对象
    NSObject *object1 = [[NSObject alloc] init];
    NSObject *object2 = [[NSObject alloc] init];

class


    NSObject *object1 = [[NSObject alloc] init];
    NSObject *object2 = [[NSObject alloc] init];
    Class objectClass1 = [object1 class];
    Class objectClass2 = [object2 class];
    Class objectClass3 = [NSObject class];
    Class objectClass4 = object_getClass(object1); // Runtime API
    Class objectClass5 = object_getClass(object2); // Runtime API

meta-class

    Class objectMetaClass = object_getClass([NSObject class]); // Runtime API

注意

    Class objectClass = [[NSObject class] class];

查看Class是否为meta-class

    
    BOOL result = class_isMetaClass([NSObject class]);

isa指针

instance(isa、其他成员便来那个)& ISA_MASK 找到 class(isa、superclass、属性、对象方法、协议、成员变量......) & ISA_MASK 找到 meta-class(isa、superclass、类方法......)

从64bit开始,isa需要进行一次位运算,才能计算出真实地址

    # if __arm64__
    #       define ISA_MASK             0x0000000ffffffff8ULL
    # elif __x86_64__
    #       define ISA_MASK             0x00007ffffffffff8ULL
    # endif

class、meta-class对象的本质结构都是 struct objc_class

struct objc_class的结构

    
    struct objc_class {
        Class isa;
        Class superclass;
        cache_t cache;  // 方法缓存
        class_data_bits_t bits; // 用于获取具体的类信息
    };
    
    // bits & FAST_DATA_MASK 找到 class_rw_t结构体
    
    struct class_rw_t {
        uint32_t flags;
        uint32_t version;
        const class_ro_t *ro;
        method_list_t * methods; // 方法列表
        property_list_t * properties; // 属性列表
        const protocol_list_t * protocols; // 协议列表
        Class firstSubclass;
        Class nextSiblingClass;
        char * demangledName;
    };
    
    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize; // instance对象占用的内存空间
    #ifdef __LP64__
        uint32_t reserved;
    #endif
        const uint8_t * ivarLayout;
        const char * name; // 类名
        method_list_t * baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars; // 成员变量列表
        const uint8_t * weakIvarLayout;
        property_list_t * baseProperties;
    };

class对象的superclass指针


@interface Student : Person
@interface Person : NSObject

Student的instance对象调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用

meta-class对象的superclass指针

    @interface Student : Person
    @interface Person : NSObject

Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用

isa、superclass总结

上一篇下一篇

猜你喜欢

热点阅读