Objective-C

234 阅读8分钟

入门

Q isa

8字节 unsigned long

nonpointer : 第一位,是否对isa指针开启指针优化,0:纯isa指针,1: 不止是类对象地址,isa中包含了类信息、对象的引用计数等

has_assoc :第二位,关联对象标志位,0没有,1存在

has_cxx_dtor : 该对象是否有c++或者Objc的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象

shiftcls : 存储类指针的值,开启指针优化的情况下,在arm64架构中有32位用来存储类指针

magic : 用于调试器判断当前对象是真的对象还是没有初始化的空间

weakly_referenced : 指对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放

deallocating : 标志对象是否正在释放内存

has_sidetable_rc : 当对象引用技术大于10时,则需要借用该变量存储进位

extra_rc : 当表示该对象的引用计数值,实际上是引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc为9,如果引用计数大于10,则需要使用到上面的has_sidetable_rc

64位。

Q alloc

graph LR
alloc --> _objc_rootAlloc --> callAlloc --> class_createinstance --> A[_class_createInstanceFromZone] --> B[cls->instanceSize]
A --> C[calloc]
A --> d[obj->initInstanceIsa]

Q mian

dyld start:

Q 对象 alloc 执行过程


->  0x102c85cd8 <+72>:  callq  0x102c86372               ; symbol stub for: objc_alloc

->  0x102c86372 <+0>: jmpq   *0x5ca0(%rip)             ; (void *)0x00007fff201a0ca4: objc_alloc

libobjc.A.dylib`objc_alloc:

libobjc.A.dylib`objc_msgSend:

001-alloc&init探索`-[ViewController viewDidLoad]:

libobjc.A.dylib`objc_retain:

libobjc.A.dylib`objc_object::sidetable_retain:

libobjc.A.dylib`objc_object::sidetable_retain:

libobjc.A.dylib`os_unfair_lock_lock_with_options:

->  0x7fff201a303c <+0>: jmpq   *0x5fe7b38e(%rip)         ; (void *)0x00007fff6da22040: os_unfair_lock_lock_with_options

libsystem_platform.dylib`os_unfair_lock_lock_with_options:

libobjc.A.dylib`+[NSObject alloc]:

->  0x7fff2019ee22 <+0>: jmp    0x7fff2019ee46            ; _objc_rootAlloc

libobjc.A.dylib`_objc_rootAlloc:

    0x7fff2019ee4f <+9>:  jmp    0x7fff20198d44            ; _objc_rootAllocWithZone

    0x7fff2019ee54 <+14>: movq   0x6631c6fd(%rip), %rsi    ; "allocWithZone:"

    0x7fff2019ee5d <+23>: jmpq   *0x5fe7f2dd(%rip)         ; (void *)0x00007fff201833c0: objc_msgSend
    
    libobjc.A.dylib`_objc_rootAllocWithZone:

    0x7fff20198d64 <+32>:  callq  0x7fff201a2f70            ; symbol stub for: calloc

    0x7fff20198db7 <+115>: jmp    0x7fff201a27d1            ; _objc_callBadAllocHandler

    0x7fff20198dcb <+135>: jmp    0x7fff20186dab            ; object_cxxConstructFromClass
    
    libsystem_malloc.dylib`calloc:

    0x7fff2016d48e <+6>:  leaq   0x66347b6b(%rip), %rdi    ; virtual_default_zone

    0x7fff2016d49a <+18>: jmp    0x7fff2016ce44            ; _malloc_zone_calloc
    
    libsystem_malloc.dylib`_malloc_zone_calloc:

    0x7fff2016ce5e <+26>:  movb   0x6634e547(%rip), %r14b   ; malloc_tracing_enabled

    0x7fff2016ce6a <+38>:  cmpl   $0x0, 0x6634e53f(%rip)    ; malloc_quarantine_enabled + 1

    0x7fff2016ce82 <+62>:  movq   0x6634e517(%rip), %rax    ; malloc_logger

    libsystem_malloc.dylib`_malloc_zone_calloc:

    

Q libobjc.A.dylib 动态库

  1. 代码断点
  2. 符号断点
  3. 汇编

Q 自定义打印

#ifdef DEBUG

#define JJNSLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);

#else

#define JJNSLog(format, ...);

#endif

Q LLDB

  • %p 打印地址
  • po 打印对象
  • x/4gx object 4个16进制
  • p/t 0x011d8001000086c1 2进制
  • p/o 0x011d8001000086c1 8进制
  • p/d 0x011d8001000086c1 10进制
  • memory read/数量格式字节数 内存地址 读取内存
  • memory write 0x0000010 10 修改内存中的值

Q 字节大小

  1. b byte 1字节
  2. h half word 2字节
  3. w word 4字节
  4. g giant word 8字节

Q 读寄存器

register read x0

Q x0

x0 : 第一个存储地方,也是返回地方;

截屏2022-06-11 上午11.49.19.png

Q alloc 原理

+ (id)alloc {

    return _objc_rootAlloc(self);

}
// Base class implementation of +alloc. cls is not nil.

// Calls [cls allocWithZone:nil].

id

_objc_rootAlloc(Class cls)

{

    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);

}
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate 

// shortcutting optimizations.

static ALWAYS_INLINE id

callAlloc(Class cls, bool checkNil, bool allocWithZone=false)

{

    if (slowpath(checkNil && !cls)) return nil;


#if __OBJC2__

    if (fastpath(!cls->ISA()->hasCustomAWZ())) {

        // No alloc/allocWithZone implementation. Go straight to the allocator.

        // fixme store hasCustomAWZ in the non-meta class and 

        // add it to canAllocFast's summary

        if (fastpath(cls->canAllocFast())) {

            // No ctors, raw isa, etc. Go straight to the metal.

            bool dtor = cls->hasCxxDtor();

            id obj = (id)calloc(1, cls->bits.fastInstanceSize());

            if (slowpath(!obj)) return callBadAllocHandler(cls);

            obj->initInstanceIsa(cls, dtor);

            return obj;

        }

        else {

            // Has ctor or raw isa or something. Use the slower path.

            id obj = class_createInstance(cls, 0);

            if (slowpath(!obj)) return callBadAllocHandler(cls);

            return obj;

        }

    }

#endif


    // No shortcuts available.

    if (allocWithZone) return [cls allocWithZone:nil];

    return [cls alloc];

}

// Replaced by ObjectAlloc

+ (id)allocWithZone:(struct _NSZone *)zone {

    return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);

}
id

_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)

{

   id obj;


#if __OBJC2__

    // allocWithZone under __OBJC2__ ignores the zone parameter

    (void)zone;

    obj = class_createInstance(cls, 0);

#else

    if (!zone) {

        obj = class_createInstance(cls, 0);

    }

    else {

        obj = class_createInstanceFromZone(cls, 0, zone);

    }

#endif

    if (slowpath(!obj)) obj = callBadAllocHandler(cls);

    return obj;

}
graph LR
alloc --> _objc_rootAlloc --> callAlloc --> A{hasCustomAWZ} 
A --> |N|B{callAllocFast} --> |Y|E[calloc obj->initInstanceIsa]
A --> |Y|C[allocWithZone] --> _objc_rootAllocWithZone --> D{zone}
B --> |N|F[class_createInstance] --> H
D --> |N|F
D --> |N|K[initIsa calloc]
D --> |Y|J[class_createInstanceFromZone] --> H[_class_createInstanceFromZone]
D --> |Y|H[malloc_zone_calloc] --> W[obj->initIsa]
H --> I{!zone && fast} --> |N|D
I --> |Y|E
D --> O[malloc_zone_calloc initIsa]

Q 消息转发机制

  1. 编译阶段
/*
不带参数:objc_msgSend(receiver,selector)   
带参数:objc_msgSend(recevier,selector,org1,org2,…)
*/
  1. 运行时阶段
/*
1. 如果selector、target两个都是有效的,那就开始查找这个类的 IMP,先从 cache 里面找,若可以找得到就跳到对应的函数去执行。
2. 如果在cache里找不到就找一下方法列表methodLists。
3. 如果methodLists找不到,就到超类的方法列表里寻找,一直找,直到找到NSObject类为止。
4. 如果还找不到,Runtime就提供了如下三种方法来处理:动态方法解析、消息接受者重定向、消息重定向
*/

Q NSProxy

/*
NSProxy 是一个抽象基类,它是为`一些作为对象的替身或者并不存在的对象`定义的API。
可以通过继承 NSProxy,并重写下面这两个方法来实现消息转发到另一个实例中。
*/

- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel

Q calloc

calloc 是对象的创建者, 指针8字节, class是一个对象,对象是一个指针。对象需要开辟多少内存空间?答案是8字节。

alignedInstanceSize() 是字节对齐函数,返回一个 word_align()函数

unalignedInstanceSize() 是没有字节对齐的函数,返回一个 data()->ro->instanceSize;

objc_class 是一个结构体,继承 objc_object

计算内存

截屏2022-06-11 下午1.19.59.png

static ALWAYS_INLINE id

callAlloc(Class cls, bool checkNil, bool allocWithZone=false)

{

#if __OBJC2__

    if (slowpath(checkNil && !cls)) return nil;

    if (fastpath(!cls->ISA()->hasCustomAWZ())) {

        return _objc_rootAllocWithZone(cls, nil);

    }

#endif

    // No shortcuts available.

    if (allocWithZone) {

        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);

    }

    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));

}

内存算法:

// X :值是8,因为是指针,上面传值过来的指针
static inline uint32_t word_align(uint32_t x) {
     return (x + WORD_MASK) & ~WORD_MASK;    
}

64位计算器 Unicode 
// WORD_MASK :值是7
// 7+8=15
// 0000 0000 0000 0111 7
// 1111 1111 1111 1000 ~7 (NOR)
// 0000 0000 0000 1111 15 
// 0000 0000 0000 1000 8 
// 1111 1111 1111 1000 & 0000 0000 0000 1111 = 0000 0000 0000 1000 
// 8 的倍数对齐

size :对象需要的内存空间 8的倍数 - 8字节对齐, 最少16字节,提高字节读取的性能。(以空间换时间)

给对象不赋值,是一个假的内存地址。

Student *s = [Student alloc];
s.name = "xiaohua";
s.age = 19;
s.height = 1.61;

// x p 以16进制打印对象的地址空间,第一个地址是 isa ,之后的地址是 name age height 
// ios 是一个小端模式,
// x/4xg p

进阶

Q 底层探索方式

  • 汇编分析
  • LLDB分析
  • 源码分析
  1. objc
  2. dyld
  3. OpenAL

Q objc4-838

Q 允许任何来源

zone 的初始化

static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)

{

    zone = runtime_default_zone();

    return zone->calloc(zone, num_items, size);

}

// p zone->calloc
// nano_calloc

static void *

nano_calloc(nanozone_t *nanozone, size_t num_items, size_t size)

{

    size_t total_bytes;

    if (calloc_get_size(num_items, size, 0, &total_bytes)) {

        return NULL;

    }

    if (total_bytes <= NANO_MAX_SIZE) {

        void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);

            if (p) {

            return p;

            } else {

            /* FALLTHROUGH to helper zone */

            }

        }

    malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);

    return zone->calloc(zone, 1, total_bytes);

}

Q 编译优化的作用

  • 编译时间:load 耗时操作
  • 链接时间
  • 运行时间
  • 空闲时间

Q obj->initInstanceIsa()

obj 指针关联类的函数, allocWithZone()

实践

Q alloc

  • _objc_rootAlloc(Class cls)
  • objc_alloc(Class cls)

截屏2022-06-13 下午4.14.25.png

Q objc_alloc

/**
系统符号表绑定: sel - imp,相当于书和目录的对应关系。函数的实现需要通过找到sel对象对应的imp,通过imp找到函数的实现。

macho - 绑定symbol - sel_alloc - objc_alloc
callalloc - [cls alloc]

objc_alloc 只走一次,修复类
imp 是函数指针, @implementation
*/

static void 

fixupMessageRef(message_ref_t *msg)

msg->imp = (IMP)&objc_alloc; 

截屏2022-06-13 下午4.26.36.png

  1. YYKit
  2. ibireme
  3. LLDB

Q clang 编译

clang -rewrite-objc main.m -o main.cpp

@encode

struct NSObject_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
}

Q RunLoop

保证线程在没有消息的时候休眠,在有消息时候唤醒,以提高程序性能。机制是依靠系统内核来完成的,苹果操作系统的核心组件 Darwin 中的 Mach.

Q 事件响应过程

苹果注册一个Source1 (基于 mach port) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()

IOKit.framework

Q 手势识别过程

__UIGestureRecognizerUpdateObserver() 回调函数处理相应处理

Q RunLoop页面渲染

beforeWaiting 进行绘制

display: 异步绘制

Backing Store 获取图形上下文

GPU 图层渲染,纹理合成

Q 事件传递与视图响应链

hitTest:

pointInside:

Q 图像显示原理

  • CPU
  1. Layout
  2. Display
  3. Prepare
  4. Commit
  • GPU
  1. 顶点着色
  2. 图元装配
  3. 光栅化
  4. 片段着色
  5. 片段处理

Q UI 卡顿

Vsync 垂直同步信号 16.7ms/帧

Q UI 绘制原理

[self.layer.delegate displayLayer: ]

bitmap

Q 离屏渲染

layer.shouldRasterize = YES;

On-Screen Rendering:当前屏幕渲染,指的是 GPU 的渲染操作是在当前用于显示的屏幕缓冲区中进行

Off-Screen Rendering:离屏渲染,分为 CPU 离屏渲染和 GPU 离屏渲染两种形式。GPU 离屏渲染指的是 GPU

在当前屏幕缓冲区外新开辟一个缓冲区进行渲染操作

应当尽量避免的则是 GPU 离屏渲染

Q YYModel 原理

  • NSObject+YYModel.h
  • YYModelPropertyMeta
  • YYModelMeta

Q 哈希算法

  • 负载均衡静态调度算法 1.轮询 2.随机 3.权重
  • 平滑轮询加权
  • 一致性哈希
  • 最小活跃数算法

Q 性能优化

  • 优化 App 电量:

    1. cpu
  • 网络方面:

    1. ProtoBuf

Q Huffman 算法

致谢

  • Cooci
  • ibireme
  • 更多开源框架的大神