美文网首页
浅谈Objective-C

浅谈Objective-C

作者: wuyou1998 | 来源:发表于2023-06-15 15:57 被阅读0次

Objective-C是一门比较古老的语言了,每一个iOSer都会使用。不知不觉参加工作已然三年,前一年半使用纯Objective-C开发,后面换工作后一直使用纯Swift。对于Objective-C,我谈不上喜欢,也不讨厌,就是觉得它设计的很巧妙。

Objective-C是C语言的超集。C(.m)和C++(.mm)可以直接写在Objective-C代码中。Objective-C就像是盖在C语言上一个薄薄的兼容层,为这门面向过程的语言带来了面向对象的能力。Runtime这个用C、汇编编写的运行时库,提供了对象模型、内存管理、消息调用、协议等一系列面向对象的概念。

由于是在C语言上拓展的面向对象,Objective-C对象的概念在C语言的基础上封装出来的。其底层实现可以简单的概括为:结构体、数组、哈希表、函数指针,以及在这些基础上的修改操作。


对象模型-类的结构

每当我们谈论到对象&类时,总少不了类、元类、属性(成员变量)、函数(消息)、生命周期这些概念。

先看一下Runtime源码中的定义。

typedef struct objc_object* id;

struct objc_object {
private:
    isa_t isa; // isa
  // 省略了一些 isa操作、内存管理、关联对象、弱引用相关的方法
};

C++中结构体struct是可以继承的,于是乎在objc_object的基础上,可以定义出类Class。

typedef struct objc_class* Class;

struct objc_class : objc_object {
    Class superclass;          // 指向父类
    cache_t cache;             // 方法缓存
    class_data_bits_t bits;    // 类的具体信息

    class_rw_t *data() { 
        return bits.data();
    }
};

Objective-C的基本对象类型是NSObject,它包含了一个Class类型成员变量isa,以及NSObject协议的实现。

@protocol NSObject
    // 协议方法省略
@end

@interface NSObject <NSObject> {
  Class isa;
}
    // 方法省略
@end

为了探究NSObject的底层实现,我们先简单定义一个类,代码如下:

// AObject.h
@interface AObject : NSObject
@property (nonatomic, copy) NSString* publicValue;
+ (void)staticMethod;
- (instancetype)initWithValue:(NSString *)value;
@end
 // AObject.m
@interface AObject ()
@property (nonatomic, copy) NSString* privateValue;
@end

@implementation AObject
@synthesize privateValue = _privateValue;

+ (void)staticMethod {
    NSLog(@"%@", @"staticMethod");
}

- (instancetype)initWithValue:(NSString *)value {
    if (self = [super init]) {
        // do some custom actions
        _publicValue = value;
    }
    return self;
}

- (void)setPrivateValue:(NSString *)privateValue {
    _privateValue = privateValue;
}

- (NSString *)privateValue {
    if (!_privateValue) {
        _privateValue = @"privateValue";
    }
    return _privateValue;
}

@end

使用下面的命令编译成cpp。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc /文件路径/AObject.m -o AObject.cpp

编译后的文件比较大,我们捡一些重点的看看。

// 实锤 NSObject 为 objc_object
typedef struct objc_object NSObject;
// 编译后的实现,可知NSObject其实是一个含有Class(objc_class)成员变量的结构体
struct NSObject_IMPL {
    Class isa;
};

再看一下Aobject的编译后的代码。

struct AObject_IMPL {
  // 父类实现
    struct NSObject_IMPL NSObject_IVARS;
  // 成员变量
  // PS: 这也是为什么可以在代码中使用 `_变量名` 取值。
    NSString *_privateValue;
    NSString *_publicValue;
};

AObject中定义的方法去哪里了呢?

// + (void)staticMethod;
static void _C_AObject_staticMethod(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_th_nyrzwfk9591fq0n11vss4v_r0000gn_T_AObject_4b2570_mi_0, (NSString *)&__NSConstantStringImpl__var_folders_th_nyrzwfk9591fq0n11vss4v_r0000gn_T_AObject_4b2570_mi_1);
}

// - (instancetype)initWithValue:(NSString *)value;
static instancetype _I_AObject_initWithValue_(AObject * self, SEL _cmd, NSString *value) {
    if (self = ((AObject *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("AObject"))}, sel_registerName("init"))) {
        (*(NSString **)((char *)self + OBJC_IVAR_$_AObject$_publicValue)) = value;
    }
    return self;
}

// - (void)setPrivateValue:(NSString *)privateValue;
static void _I_AObject_setPrivateValue_(AObject * self, SEL _cmd, NSString *privateValue) {
    (*(NSString **)((char *)self + OBJC_IVAR_$_AObject$_privateValue)) = privateValue;
}
// - (NSString *)privateValue
static NSString * _I_AObject_privateValue(AObject * self, SEL _cmd) {
    if (!(*(NSString **)((char *)self + OBJC_IVAR_$_AObject$_privateValue))) {
        (*(NSString **)((char *)self + OBJC_IVAR_$_AObject$_privateValue)) = (NSString *)&__NSConstantStringImpl__var_folders_th_nyrzwfk9591fq0n11vss4v_r0000gn_T_AObject_4b2570_mi_2;
    }
    return (*(NSString **)((char *)self + OBJC_IVAR_$_AObject$_privateValue));
}

// 自动生成的 publicValue 的 gettter
static NSString * _I_AObject_publicValue(AObject * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_AObject$_publicValue)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

// 自动生成的 publicValue 的 settter
static void _I_AObject_setPublicValue_(AObject * self, SEL _cmd, NSString *publicValue) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct AObject, _publicValue), (id)publicValue, 0, 1); }

基于此,我们可以简单的了解到OC中类的设计。类 = 父类实现 + 变量 + 一组方法实现

由于C语言的限制,结构体中无法定义函数,但可以定义函数指针,所以OC类中的方法的真实实现其实都是包含了静态函数指针的结构体。静态方法和实例方法区别在于函数的第一个参数是Class还是结构体指针。

下面是一张老生常谈的图,iOSer估计都看过。

2019053019152181.png

这种类、元类以及类之间继承的机制是怎么实现的呢?

我们先回顾objc_class的实现,它继承自objc_object,包含了指针isa、父类superClass、方法缓存cache、类信息bits。毫无疑问,示例代码中定义的AObject自然是有这些的。那么这些变量肯定也是要初始化的。

在继续讲类的设计之前,我们先花一点时间,了解一下Objective-C中函数的设计。

typedef struct method_t* Method;

struct method_t {
    SEL name;                   // 方法名
    const char *types;  // 使用Type Encodings 编码技术,将函数编码成字符串
    IMP imp;                    // 函数指针,指向具体的实现
};

// method_t 编译成 cpp的形式,method_t和_objc_method实际是一样的
struct _objc_method {
    struct objc_selector * _cmd;
    const char *method_type;
    void  *_imp;
};

// objc_selector没有找到具体实现,推测只是函数名称,作为identifier使用
typedef struct objc_selector* SEL;

// IMP
typedef void (*IMP)(void /* id, SEL, ... */ ); 

值得一提的是,SEL维护在一个全局的表中,全局唯一。不同类中相同名字的SEL是相同的,IMP不同,所以需要使用Method来做映射。

我们以AObject中的静态方法为例staticMethod为例,其编译之后的结构体如下:

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_AObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"staticMethod", "v16@0:8", (void *)_C_AObject_staticMethod}}
};

接着根据_OBJC_$_CLASS_METHODS_AObject去初始化元类的class_ro_t(bits的一部分,我们这个类比较简单,编译后没有用到的部分去除了)

static struct _class_ro_t _OBJC_METACLASS_RO_$_AObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1, sizeof(struct _class_t), sizeof(struct _class_t), 
    0, 
    "AObject",
    (const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_AObject,
    0, 
    0, 
    0, 
    0, 
};

再然后去初始化元类的信息。

extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_AObject __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_NSObject,
    0, // &OBJC_METACLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_METACLASS_RO_$_AObject,
};

同理,使用实例方法去初始化类的信息。

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[7];
} _OBJC_$_INSTANCE_METHODS_AObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    7,
    {{(struct objc_selector *)"initWithValue:", "@24@0:8@16", (void *)_I_AObject_initWithValue_},
        {(struct objc_selector *)"setPrivateValue:", "v24@0:8@16", (void *)_I_AObject_setPrivateValue_},
        {(struct objc_selector *)"privateValue", "@16@0:8", (void *)_I_AObject_privateValue},
        {(struct objc_selector *)"publicValue", "@16@0:8", (void *)_I_AObject_publicValue},
        {(struct objc_selector *)"setPublicValue:", "v24@0:8@16", (void *)_I_AObject_setPublicValue_},
        {(struct objc_selector *)"publicValue", "@16@0:8", (void *)_I_AObject_publicValue},
        {(struct objc_selector *)"setPublicValue:", "v24@0:8@16", (void *)_I_AObject_setPublicValue_}}
};

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_AObject __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_AObject,
    0, // &OBJC_CLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_AObject,
};

搞到这里,类的设计基本就清晰了:类是结构体,类的每一部分也都是结构体。

最后在setup函数中对类进行复制,设置其他的变量。

// 可以根据这个体会下上面那张老生常谈的图:)
static void OBJC_CLASS_SETUP_$_AObject(void ) {
    OBJC_METACLASS_$_AObject.isa = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_AObject.superclass = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_AObject.cache = &_objc_empty_cache;
    OBJC_CLASS_$_AObject.isa = &OBJC_METACLASS_$_AObject;
    OBJC_CLASS_$_AObject.superclass = &OBJC_CLASS_$_NSObject;
    OBJC_CLASS_$_AObject.cache = &_objc_empty_cache;
}

将函数指针注册,初始化Objc的时候进行统一调用。

PS:减少OC无用类,即可减少数组中的函数指针个数,进而优化启动时间。

#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CLASS_SETUP[] = {
    (void *)&OBJC_CLASS_SETUP_$_AObject,
};

行文至此,我们可以简单总结一下:AObject* obj = [AObject new];实际上是创建了一个OBJC_CLASS_$_AObject的结构体指针。

类Class中存储了实例方法(的地址),元类MetaClass中存储了静态方法(的地址)。类和元类、类的继承结构其实是在Objc初始化时调用setup函数进行赋值的。

属性property本质上就是成员变量ivar,但在其上做了一些字符串编码,用于表示属性的一些特性。eg: copy, nonatomic。

talk is cheap,看下下面的代码就明白啦,以AObject为例。

struct _ivar_t {
    unsigned long int *offset;  // pointer to ivar offset location
    const char *name;
    const char *type;
    unsigned int alignment;
    unsigned int  size;
};

struct _prop_t {
    const char *name;
    const char *attributes;
};

static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[2];
} _OBJC_$_INSTANCE_VARIABLES_AObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    2,
    {{(unsigned long int *)&OBJC_IVAR_$_AObject$_privateValue, "_privateValue", "@\"NSString\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_AObject$_publicValue, "_publicValue", "@\"NSString\"", 3, 8}}
};

static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_AObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"publicValue","T@\"NSString\",C,N,V_publicValue"}}
};

消息(函数)则是以method_t结构体的形式存储在class_ro_t结构体中的method_list中。

关于对象的生命周期,我们知道,C语言需要手动管理内存的开辟与释放,所以OC需要一套内存管理机制。以前是手动管理,MRC(Manual Reference Count),现在是自动管理,ARC(Automatic Reference Count)。这部分主要是介绍对象模型,后面会单开一节介绍内存管理。


对象模型-isa

上文中,我们简单了解了下class_ro_tisa,下面我们详细介绍下这部分。

class_data_bits

Class_data_bits是对class_rw_t的封装,通过bits & FAST_DATA_MASK取到。

struct class_data_bits_t {
    // Values are the FAST_ flags above.
    uintptr_t bits;
public:
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

class_rw_t是对class_ro_t的封装,存储了类的一些可读写的信息。方法列表、属性列表、协议列表都是二维数组(基于二维数组实现了分类的机制)。

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;       // 方法列表
    property_array_t properties;  // 属性列表
    protocol_array_t protocols;   // 协议列表

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
};

class_ro_t表示类的只读信息,包含成员变量列表,类名等,是类最初始的内容。baseMethods、baseProtocols、ivars、properties都是一维数组。

struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    const unsigned char *ivarLayout;
    const char *name;
    const struct _method_list_t *baseMethods;
    const struct _objc_protocol_list *baseProtocols;
    const struct _ivar_list_t *ivars;
    const unsigned char *weakIvarLayout;
    const struct _prop_list_t *properties;
};

当程序运行时,会在Objc初始化的方法中经过一系列的函数,在realizeClass()方法中将class_ro_t和分类的内容合并,存储在class_rw_t里。

isa

isa在64位系统上是一个联合体。其中33位用于存储class/meta-class的内存地址信息。其余位置存储一些特定的信息。

struct objc_object {
    Class isa;  // 在 arm64 架构之前
};

struct objc_object {
private:
    isa_t isa;  // 在 arm64 架构开始
};

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

#if SUPPORT_PACKED_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // nonpointer must be the LSB (fixme or get rid of it)
    // shiftcls must occupy the same bits that a real class pointer would
    // bits + RC_ONE is equivalent to extra_rc + 1
    // RC_HALF is the high bit of extra_rc (i.e. half of its range)

    // future expansion:
    // uintptr_t fast_rr : 1;     // no r/r overrides
    // uintptr_t lock : 2;        // lock for atomic property, @synch
    // uintptr_t extraBytes : 1;  // allocated with extra bytes

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL  // 用来取出 Class、Meta-Class 对象的内存地址
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1;  // 0:代表普通的指针,存储着 Class、Meta-Class 对象的内存地址
                                          // 1:代表优化过,使用位域存储更多的信息
        uintptr_t has_assoc         : 1;  // 是否有设置过关联对象,如果没有,释放时会更快
        uintptr_t has_cxx_dtor      : 1;  // 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
        uintptr_t shiftcls          : 33; // 存储着 Class、Meta-Class 对象的内存地址信息
        uintptr_t magic             : 6;  // 用于在调试时分辨对象是否未完成初始化
        uintptr_t weakly_referenced : 1;  // 是否有被弱引用指向过,如果没有,释放时会更快
        uintptr_t deallocating      : 1;  // 对象是否正在释放
        uintptr_t has_sidetable_rc  : 1;  // 如果为1,代表引用计数过大无法存储在 isa 中,那么超出的引用计数会存储在一个叫 SideTable 结构体的 RefCountMap(引用计数表)哈希表中
        uintptr_t extra_rc          : 19; // 里面存储的值是引用计数 retainCount - 1
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };
};

对象模型-分类

这部分介绍下分类的实现原理。我们现在为AObject写一个分类,代码如下所示。

@interface AObject (extension)
@property (nonatomic, strong) NSObject* bObject;
- (void)extensionMethod;
@end

const static void* bObjectKey = @"bObjectKey";
@implementation AObject (extension)

- (void)setBObject:(NSObject *)bObject {
    objc_setAssociatedObject(self, bObjectKey, bObject, OBJC_ASSOCIATION_RETAIN);
}

- (NSObject *)bObject {
    return objc_getAssociatedObject(self, bObjectKey);
}

- (void)extensionMethod {
    // 分类方法实现
}
@end

接着同样编译成C代码,相较之前多了如下部分。

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[3];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_AObject_$_extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    3,
    {{(struct objc_selector *)"setBObject:", "v24@0:8@16", (void *)_I_AObject_extension_setBObject_},
    {(struct objc_selector *)"bObject", "@16@0:8", (void *)_I_AObject_extension_bObject},
    {(struct objc_selector *)"extensionMethod", "v16@0:8", (void *)_I_AObject_extension_extensionMethod}}
};

static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_AObject_$_extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"bObject","T@\"NSObject\",&,N"}}
};

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_AObject;

static struct _category_t _OBJC_$_CATEGORY_AObject_$_extension __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "AObject",
    0, // &OBJC_CLASS_$_AObject,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_AObject_$_extension,
    0,
    0,
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_AObject_$_extension,
};
static void OBJC_CATEGORY_SETUP_$_AObject_$_extension(void ) {
    _OBJC_$_CATEGORY_AObject_$_extension.cls = &OBJC_CLASS_$_AObject;
}
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CATEGORY_SETUP[] = {
    (void *)&OBJC_CATEGORY_SETUP_$_AObject_$_extension,
};
static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {
    &OBJC_CLASS_$_AObject,
};
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
    &_OBJC_$_CATEGORY_AObject_$_extension,
};

和class_t类似,编译后生成了分类的方法列表、属性列表(但是没有ivar),也在OBJC_CATEGORY_SETUP注册了回调。会在Objective-C初始化时进行类和分类的合并。类的基本信息class_ro_t和_category_t进行合并,最后组装成了class_rw_t。合并的代码里,分类的method_list会在本类的前面,这样在查找的时候就会优先被找到,相当于"覆盖"了原来的实现。


消息机制

在上文中,可以知道OC对象的方法,在C语言层面都是静态函数。C静态函数参数为结构体指针、SEL、真实参数(如有)。

Objective-C中的方法调用,使用的是message_send。

// 参数一:对象或者叫消息接受者
// 参数二:SEL
// 可变长参数:SEL参数
void objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

函数参数了列表和OC方法编译后的格式一样。message_send调用过程分为消息调用动态消息解析消息转发三个阶段。

大致流程如下图所示。图来自师大小海腾,本文参考了他很多的内容。

image.png
消息调用

源码分析的网上有很多,这里只是简单说下实现。

1、先根据消息接受者的类型是instance还是class,决定是从Class还是MetaClass中查找。

2、先从cache中找,找到了直接调用。没有就从class_rw_t的方法列表查找。

2、class_rw_t的方法列表未排序就遍历查找、已排序就二分查找。找到了就调用,并将方法缓存在cache中。

3、未找到就从superClass继续查找,查找流程同2、3。

4、superClass遍历到头了还是找不到,进行动态消息解析阶段。

动态消息解析

这一阶段会根据实例方法、类方法来调用如下方法(按需重写)。

+(BOOL)resolveInstanceMethod:(SEL)sel;
+(BOOL)resolveClassMethod:(SEL)sel;

一般在方法内部搭配addMethod来动态添加方法实现。

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

resolveInstanceMethod返回YES后,会重新进行一次查找,从cache开始查。查完没找到方法,并且也经过一次动态消息解析了,那么开始进行消息转发阶段。

消息转发

这一步分为快速转发Fast forwarding 和正常转发 Normal forwarding

Fast forwarding需要返回一个不同于自身的备用接受者。

+/- (id)forwardingTargetForSelector:(SEL)sel {
    return 备用接收者;
}

Normal forwarding需要重写两个方法。methodSignatureForSelector需要返回一个具体的方法签名,Runtime会根据方法签名来生成一个具体的NSInvocation对象。forwardInvocation中可以使用生成的NSInvocation对象来做一些自定义操作。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return 方法签名;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    // 一些自定义操作
}

看一下系统默认实现,经典的unrecognized selector sent to instance错误~

// NSObject.mm
+ (id)forwardingTargetForSelector:(SEL)sel {
    return nil;
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    _objc_fatal("+[NSObject methodSignatureForSelector:] "
                "not available without CoreFoundation");
}
+ (void)forwardInvocation:(NSInvocation *)invocation {
    [self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
}
+ (void)doesNotRecognizeSelector:(SEL)sel {
    _objc_fatal("+[%s %s]: unrecognized selector sent to instance %p", 
                class_getName(self), sel_getName(sel), self);
}

对象生命周期

简单来讲,创建一个实例对象,以及实例对象的析构操作,本质上对应的是内存的开辟malloc与回收free

引用计数

Objective-C中的内存管理是基于引用计数来实现的,将对象的引用计数保存起来,当引用计数为0时,就将其释放。引用计数保存在isa中。前文提过,isa是一个64位的联合体,其中33位存储着 Class、Meta-Class 对象的内存地址信息,引用计数使用19位进行存储,如果19位不够实用,则存储在SideTable中。

早期的引用计数需要开发者手动控制,称为MRC,Manual Reference Counting。

现在则是编译器处理了,称为ARC,Automatic Reference Counting。

ARC几乎让开发者对于内存管理无感知,但少数情况下,处理与CoreFoundation桥接时(Toll-Free Bridging),会处理到一些手动内存管理的情况。

这里不做过多介绍,想要了解可以搜Toll-Free Bridging,相关文章很多。

自动释放

引用计数为0的时候,对象就会立即释放,可以使用@autoreleasepool来推后释放时机。MRC中使用[object autorelease],并不会立即使得object的引用计数-1,而是加入到autoreleasepool中。在runloop快要结束时,autoreleasepool会将其内部对象统一调用[object release]。

还有一种方式是使用@autoreleasepool{}包裹代码,这样当代码出了@autoreleasepool作用域时,就会release。假设一个循环中创建了大量临时对象,通过这种手段可以避免内存使用量快速增高,达到内存削峰的目的。

__weak

引用计数无法处理双向持有的问题,这个时候需要使用弱引用来打破内存的持有环。

TaggedPointer

对于一些量比较小的数据,可以直接存在指针里,不需要在堆上开辟内存。


一些没有提及的部分

文章写到这里,基本上也就是蜻蜓点水的过了一遍Objective-C的一些设计吧,比较宏观,没有做深入细致的讲解。

有关self和super、属性关键字&修饰符、KVO&KVC、方法缓存查找&添加、对象析构过程、messageSend的具体代码过程、initialize&load、关联对象、weak原理、AutoReleasePool原理等,本来想写,字太多就跳过去了(不面试我记不住)。

如果有需要,大家可以拿着关键字去搜一下,很多博客。

相关文章

网友评论

      本文标题:浅谈Objective-C

      本文链接:https://www.haomeiwen.com/subject/itslydtx.html