iOS 底層探索篇 —— 對象的本質

前言html

對象的本質

1. 準備代碼

@interface XDPerson : NSObject
{
    NSString *nickName; //成員變量
//    UILabel *label;   //實例變量 是特殊的成員變量 經過類實例出來的
}
@property (nonatomic,copy) NSString *name; //屬性
@end

@implementation XDPerson

@end
複製代碼

咱們準備一份這樣的代碼放在main.m文件裏面,經過clang -rewrite-objc main.m -o main.cpp來編譯成mian.cpp文件。bash

2. 分析編譯生成的main.cpp文件

咱們直接看與XDPerson相關的代碼app

struct NSObject_IMPL {
    Class isa;
};
···
#ifndef _REWRITER_typedef_XDPerson
#define _REWRITER_typedef_XDPerson
typedef struct objc_object XDPerson;
typedef struct {} _objc_exc_XDPerson;
#endif

extern "C" unsigned long OBJC_IVAR_$_XDPerson$_name;
struct XDPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *nickName;
    NSString *_name;
};
// @property (nonatomic,copy) NSString *name;
/* @end */

// @implementation XDPerson
static NSString * _I_XDPerson_name(XDPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_XDPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_XDPerson_setName_(XDPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct XDPerson, _name), (id)name, 0, 1); }
// @end
複製代碼
  • XDPerson這個類在底層編譯成了XDPerson_IMP結構體。
  • 成員NSObjct_IVARS,這是一個結構體,裏面只包含了isa
  • 成員nickName,是咱們類裏面的成員變量。
  • 成員_name,是咱們類裏面的屬性,編譯成了帶有_下劃線的成員。
  • 函數_I_XDPerson_name,實際上就是getter方法,包含兩個默認參數self_cmd
  • 函數_I_XDPerson_setName_,實際上就是setter方法,包含兩個默認參數self_cmd,與一個形參name

根據編譯的結構咱們能夠得出下面結論:iphone

  • 對象的本質在底層就是一個結構體。
  • 屬性與成員變量的區別,屬性是由成員變量+getter方法+setter方法組成。

3. 分析method_list_t結構體

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[2];
} _OBJC_$_INSTANCE_METHODS_XDPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    2,
    {{(struct objc_selector *)"name", "@16@0:8", (void *)_I_XDPerson_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_XDPerson_setName_}}
};
複製代碼
  1. 咱們分析一下這兩個objc_selector
  • {{(struct objc_selector *)"name", "@16@0:8", (void *)_I_XDPerson_name},
  • {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_XDPerson_setName_}}

兩個都是由SEL->selectortype方法簽名imp函數實現組成。ide

  1. type方法簽名介紹

咱們拿v24@0:8@16分析:函數

  • 第一個符號v返回值類型,24返回值類型的長度。
  • 第二個符號@參數一類型對象0長度0-7
  • 第三個符號:參數二類型SEL8長度8-15
  • 第四個符號@參數三類型對象16長度16-23

這裏咱們能夠經過打印一下各類類型來獲取各類符號typeEncodepost

NSLog(@"char--%s",@encode(char));
    NSLog(@"short--%s",@encode(short));
    NSLog(@"int--%s",@encode(int));
    NSLog(@"long--%s",@encode(long));
    NSLog(@"long long--%s",@encode(long long));
    NSLog(@"unsigned char--%s",@encode(unsigned char));
    NSLog(@"unsigned short--%s",@encode(unsigned short));
    NSLog(@"unsigned int--%s",@encode(unsigned int));
    NSLog(@"unsigned long--%s",@encode(unsigned long));
    NSLog(@"float--%s",@encode(float));
    NSLog(@"BOOL--%s",@encode(BOOL));
    NSLog(@"void--%s",@encode(void));
    NSLog(@"char *--%s",@encode(char *));
    NSLog(@"id--%s",@encode(id));
    NSLog(@"Class--%s",@encode(Class));
    NSLog(@"SEL--%s",@encode(SEL));
    int array[] = {1,2};
    NSLog(@"int[]--%s",@encode(typeof(array)));
    typedef struct person{
        int age;
        NSString *name;
    }Person;
    NSLog(@"struct--%s",@encode(Person));
    
    typedef union teacher{
        char *a;
        BOOL b;
    }Teacher;
    NSLog(@"union--%s",@encode(Teacher));

2019-12-19 20:47:51.143558+0800 Object本質[2623:63103] char--c
2019-12-19 20:47:51.143887+0800 Object本質[2623:63103] short--s
2019-12-19 20:47:51.143906+0800 Object本質[2623:63103] int--i
2019-12-19 20:47:51.143966+0800 Object本質[2623:63103] long--q
2019-12-19 20:47:51.144035+0800 Object本質[2623:63103] long long--q
2019-12-19 20:47:51.144080+0800 Object本質[2623:63103] unsigned char--C
2019-12-19 20:47:51.144108+0800 Object本質[2623:63103] unsigned short--S
2019-12-19 20:47:51.144131+0800 Object本質[2623:63103] unsigned int--I
2019-12-19 20:47:51.144153+0800 Object本質[2623:63103] unsigned long--Q
2019-12-19 20:47:51.144173+0800 Object本質[2623:63103] float--f
2019-12-19 20:47:51.144193+0800 Object本質[2623:63103] BOOL--c
2019-12-19 20:47:51.144213+0800 Object本質[2623:63103] void--v
2019-12-19 20:47:51.144234+0800 Object本質[2623:63103] char *--*
2019-12-19 20:47:51.144256+0800 Object本質[2623:63103] id--@
2019-12-19 20:47:51.144277+0800 Object本質[2623:63103] Class--#
2019-12-19 20:47:51.144297+0800 Object本質[2623:63103] SEL--:
2019-12-19 20:47:51.144317+0800 Object本質[2623:63103] int[]--[2i]
2019-12-19 20:47:51.144337+0800 Object本質[2623:63103] struct--{person=i@}
2019-12-19 20:47:51.144359+0800 Object本質[2623:63103] union--(teacher=*c)
複製代碼

咱們能夠直接經過蘋果官網TypeEncode或者在Xcode中在@Encodecommond+shift+0來查看。ui

viewDidLoad的本質

1. 準備代碼

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}
@end
複製代碼

2. clang編譯文件

這裏若是還用上面的方式來編譯,應該是會報錯的,須要用下面的方式atom

  • xcrun -sdk iphonesimulator clang -rewrite-objc ViewController.m
  • xcrun -sdk iphoneos clang -rewrite-objc ViewController.m
  1. 查看編譯以後的ViewController.cpp文件
#ifndef _REWRITER_typedef_ViewController
#define _REWRITER_typedef_ViewController
typedef struct objc_object ViewController;
typedef struct {} _objc_exc_ViewController;
#endif

struct ViewController_IMPL {
    struct UIViewController_IMPL UIViewController_IVARS;
};
/* @end */

// @interface ViewController ()

/* @end */

// @implementation ViewController

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
}

struct UIViewController_IMPL {
    struct UIResponder_IMPL UIResponder_IVARS;
};
// @end
複製代碼
  • ViewController在底層也被編譯成告終構體。惟一成員是繼承自父類的成員。
  • viewDidLoad方法在底層編譯成_I_ViewController_viewDidLoad函數,默認也是兩個參數self_cmd
  • super viewDidLoad方法在底層編譯成((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));,這個函數簡化以後objc_msgSendSuper(self.super,viewDidLoad),其本質就是給父類發送viewDidLoad這麼一個消息。
  1. method_list_t結構體查看
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_INSTANCE_METHODS_ViewController __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"viewDidLoad", "v16@0:8", (void *)_I_ViewController_viewDidLoad}}
};
複製代碼

能夠看到一樣也SEL->selector+typeEncode+imp組成。spa

對象和類在內存中的數量

1. 對象的數量

XDPerson *p1 = [XDPerson alloc];
    XDPerson *p2 = [XDPerson alloc];
    XDPerson *p3 = [XDPerson alloc];
    NSLog(@"p1-%p",p1);
    NSLog(@"p2-%p",p2);
    NSLog(@"p3-%p",p3);
    
    2019-12-19 21:42:33.901531+0800 alloc探索[3108:105948] p1-0x600000639da0
    2019-12-19 21:42:33.901750+0800 alloc探索[3108:105948] p2-0x60000063ae20
    2019-12-19 21:42:33.901878+0800 alloc探索[3108:105948] p3-0x600000639620
複製代碼

經過輸出結果能夠看到對象alloc一次在系統內存中就會增長一份。

2. 類的數量

Class c1 = [XDPerson class];
    Class c2 = [XDPerson alloc].class;
    Class c3 = object_getClass([XDPerson alloc]);
    NSLog(@"c1-%p",c1);
    NSLog(@"c2-%p",c2);
    NSLog(@"c3-%p",c3);
    
    2019-12-19 21:42:33.901996+0800 alloc探索[3108:105948] c1-0x10b8f30e8
    2019-12-19 21:42:33.902107+0800 alloc探索[3108:105948] c2-0x10b8f30e8
    2019-12-19 21:42:33.902205+0800 alloc探索[3108:105948] c3-0x10b8f30e8
複製代碼

經過輸出結果能夠看到類的數量在系統內存中只有一份。

相關文章
相關標籤/搜索