导言

​ 本系列接《Effective Objective-C 2.0》一书中的系列文章。

本文主要针对几个类来窥探实例对象在内存中的存储,我们从成员变量和属性入手,本文相关代码在这儿

我们平时编写的Objective-C代码,底层实现其实都是C\C++代码

屏幕快照 2018-11-08 下午7.24.53

所以Objective-C的面向对象都是基于C\C++的数据结构实现的

Objective-C的对象、类主要是基于C\C++的什么数据结构实现的——结构体。

阅读全文 »

我们经常需要在开发中判定某一个类,比如下面场景:

  • 判定在某一个页面:isMemberOfClass来指定只有在某页面下的操作。

  • 判断是否某个类,用于容错,这很常见。

    1
    2
    3
    if([BFUserModel isKindOfClass:BFModel]) {
    //.....针对user model
    }

这些涉及到类的判定的,我们下面会从一个关键字和两个方法去阐述。

  • super关键字:调用父类方法的关键字
  • isMemberOfClass方法:判断某个instance(class)是否是对应的class(meta-class)对象
  • isKindOfClass方法:判断某个instance(class)是否是对应的class(meta-class)继承体系下的对象。
阅读全文 »

一、介绍与应用

1.1 objc_msgSend

Objective-C中调用方法,称为消息传递,消息有名称(name)选择子(selector),可以接收参数,而且可能还有返回值。

objc_msgSend其实就是消息传递在底层C语言的函数实现,在Objective-C中,大部分方法调用都是经过objc_msgSend来实现的。当然,除去load方法·等特殊情况。

一般,给对象发送消息如下:

1
id retrunValue = [someObject messageName:parameter];

someObject是消息接收者,messageName是选择子选择子参数合起来称为消息。在底层,编译器收到之后,将其转换obj_msgSend函数,其函数声明如下:

1
void objc_msgSend(void /* id self, SEL op, ... */ )
  • 第一个参数为消息接收者
  • 第二个参数为SEL

上面给对象发送之后转换即为:

1
id retrunValue = objc_msgSend(someObject, @selector(messageName:), parameter);

objc_msgSend会根据接受者与选择子的类型来调用适当的方法。

阅读全文 »

在了解类的基本结构之后,本文开始了解探讨iOS 中的消息发送,即消息调用。

首先开始讨论的是——在真正消息调用之前,我们会去方法缓存里面寻找真实的函数地址,iOS提供的缓存机制用于提高效率。

For speed, objc_msgSend does not acquire any locks when it reads method caches. Instead, all cache changes are performed so that any objc_msgSend running concurrently with the cache mutator will not crash or hang or get an incorrect result from the cache.

一、方法的相关结构

1.1 回顾Class结构

不管讨论,什么离不开最基本的类结构,现在我们又回到Class结构,不过这次需要关注的是方法

阅读全文 »

一、基础

在开始探讨isa指针之前,我们要准备一些基础知识,包括位域、联合体以及内存分配的相关知识。

1.1 位域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义位段
struct mybitfields
{
unsigned short a : 4;
unsigned short b : 5;
unsigned short c : 7;
} test;

int main( void );
{
test.a = 2;
test.b = 31;
test.c = 0;
}

//最后如下显示
00000001 11110010
cccccccb bbbbaaaa

位域表示的是,在一个结构体中,用位来存储数据。

关于位域的内存分配,有几个值得注意的点:

  • 位域的最小内存不能小于位域中最大的修饰符,上面实例都是short,即最小不能小于short的大小;
  • 位域的最小内存不能小于所有位域字段加起来的大小,并且根据内存对齐,可能会分配多余。假设所有位域字段加起来为18位,那么就会分配32位,即4字节。
  • 位域中如果有无名位域,那么无名位域会强制下一位域对齐到其下一type位域的边界。(C/C++位域结构深入解析
阅读全文 »

从这篇开始,我们要开始讲述Runtime中的一些主要节点,打通这些节点,我们将能从容应对日常开发中遇到的疑难杂症。

本系列文章同样参考官方文档、MJ老师等资料。

一、Runtime是什么

Objective-C Runtime Programming Guide 首先阐明了runtime:

The Objective-C language defers as many decisions as it can from compile time and link time to runtime.

Objective-C语言,会尽量将决议放到运行的时候,而不是在编译和链接过程。

runtime就是运行时提供的一套机制。那么这个机制具体指的是什么?

  • 从代码角度,这是一套底层的 C 语言 API,平时编写的Objc代码,都是基于它来实现的。
    • 下面的源码编译及后续对API解读,就是基于此进行的说明。
  • 从编译运行看,runtime提供了一系列特性,主要是消息机制保证了Objc具有强大的运行时动态变化的能力。
    • 我们会对这些强大的动态能力,作更详细的解读。
阅读全文 »

假如你已经阅读完前面的三篇RunLoop的文章,就会对RunLoop是什么,有一个比较全貌的了解。

本文,将针对RunLoop在iOS/macOS中的应用,做一些常用场景的分析。

一、线程保活

线程保活,就是利用RunLoop的运行循环,使得某个线程能长时间的运行,在有任务时执行任务,没有任务时也保持运行,不退出线程。

相关内容,将会在另外一篇 占位待补:多线程(九)线程保活中详细描述。

阅读全文 »

在之前的篇章中,对RunLoop有了基本认识,以及对其底层的对象也有了一定了解。

这篇描述的是”RunLoop是如何运行的?‘’

为了解决这个问题,我们还是直接上源码,并给出导读的路径,如下:

★ Core Foundation 下载地址

★ CFRounLoop.c

__CFRunLoopRun

​ >>>CFRunLoopRunSpecific

​ >>>CFRunLoopRun

★ 源码注读 下载地址

阅读全文 »

在上文,我们了解到RunLoop不但表示的是一个事件循环,也是一个对象。

在本文,我们将会在源码的帮助下,窥探RunLoop对象,以及其相关对象。

源码的出处:CF源码Swift CF源码。笔者注读的源码:SourceCode

其中,关于RunLoop的类有:

一、RunLoop对象

1. CFRunLoopRef

获取RunLoop对象可以通过下面获得:

1
2
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
阅读全文 »

本系列文章,主要参考了MJ老师的视频、ibireme的博客深入理解RunLoop 以及官方文档说明,表示感谢。

一、引子

1. 不同的应用程序

我们在开发iOS App或者macOS Cocoa App时,常见的main函数,只包含如下代码:

1
2
3
4
5
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
阅读全文 »

本文分为三部分:

两节主要讲述自动释放池的概念和应用,其中二中还详述了autorelease与方法返回值的关系。其中涉及的示例代码在自动释放池-应用

探索了自动释放池的原理,但是没有对源码进行更多的描述,只是阐述了机制的运作。相关示例代码在自动释放出-原理

本文涉及到的objc源码,来自于objc源码,版本是723。

阅读全文 »

本文将主要讲述拷贝这个操作以及copy关键字,大部分是实际代码应用的部分,示例代码参考-copy

一、拷贝

关于拷贝,要了解两个点:

  • 为什么要拷贝?
  • 如何拷贝?

image-20190107091218999

阅读全文 »