1.运行时
  • OC 语言由 Smalltalk(20世纪70年代出现的一种面向对象的语言) 演化而来,后者是消息型语言的鼻祖。
  • OC 使用动态绑定的消息结构,在运行时检查对象类型。
  • 使用消息结构的语言,其运行时执行的代码由运行环境来决定。而使用函数调用的语言,则由编译器决定。
  • OC 对象所占内存总是分配在“堆空间”,绝不会分配在“栈”上。分配在堆中的内存必须直接管理,而分配在栈上用于保存变量的内存会在栈帧弹出时自动清理。
  • OC 运行期内存管理架构,名叫“引用计数”。
  • OC 中会遇到定义中不含 * 的变量,也是会使用“栈空间”。比如 CoreGraphics 框架的 CGRect,整个系统框架都在使用这种结构体,如果使用 OC 对象,性能会受影响。
2.属性
  • 用来封装对象中的数据。
  • 如果使用了属性的话,那么编译器就会主动编写访问这些属性所需的方法,此过程叫做“自动合成”。这个过程在编译期执行,点语法是编译时特性。
  • @synthesize 语法来指定实例变量的名字。
@synthesize testString = _testString;
  • @dynamic 关键字会告诉编译器不要创建实现属性所用的实例变量,也不要为其创建存取方法。而且,在编译访问属性的代码时,即使编译器发现没有定义存取方法也不会报错,它相信这些方法能在运行时找到。
@dynamic testString;
_testString //Use of undeclared identifier '_testString'
  • 属性特质的设定也会影响编译器所生成的存取方法。属性特质包括:原子性、读写属性、内存管理语义(assign、strong、weak、unsafe_unretained、copy)、方法名(getter==…)。
  • 如果想在其他方法里设置属性值,同样要遵守属性定义中所宣称的语义,因为“属性定义”就相当于“类”和“待设置的属性值”之间所达成的契约

    比如在指定构造器中对成员变量的赋值
@interface TestObject ()
//虽然这个属性已经是只读性质,也要写上具体的语义,以此表明初始化方法在设置这些值时所用方法
@property(copy,readonly) NSString *testString;
@end @implementation TestObject
- initWithString:(NSString *)string { self = [super init]; if (self) {
//用初始化方法设置好属性值之后,就不要再改变了,此时属性应设为“只读”
_testString = [string copy];
}
return self;
}
@end
3.对象等同性
  • “==”操作符比较的是两个指针本身,而不是所指的对象。应该使用 NSObject 协议中声明的“isEqual”方法来判断两个对象的等同性。
    NSString *textA = @"textA";
NSString *textAnother = [NSString stringWithFormat:@"textA"];
NSLog(@"%d",textA == textAnother);// 0
NSLog(@"%d",[textA isEqual:textAnother]);// 1
NSLog(@"%d",[textA isEqualToString:textAnother]);// 1
  • 在自定义的对象中正确复写“isEqual”"hash"方法,来判定两个方法是否相等。
  • 如果 “isEqual”方法判定两个对象相等,那么其 hash 方法也必须返回同一个值。

    比如下面这个类
@interface TestObject : NSObject
@property NSString *testString;
@end @implementation TestObject
- (BOOL)isEqual:(id)object {
if (self == object) return YES;
if ([self class] != [object class]) return NO; TestObject *otherObject = (TestObject *)object;
if (![self.testString isEqualToString:otherObject.testString]) {
return NO;
}
return YES;
}
-(NSUInteger)hash {
//在没有性能问题下,hash 方法可以直接返回一个数
return 1227;
} @end

在继承体系中判断等同性,还需判断是否是其子类

相同的对象必须具有相同的哈希码,但是相同哈希码的对象却未必相同

特定类型等同性判断
  • 自己创建等同性判断方法,无需检测参数类型,大大提升检测速度。就像“isEqualToString”一样。
- (BOOL)isEqualToTestObject:(TestObject *)testobject {

    if (self == testobject) {
return YES;
}
if (![self.testString isEqualToString:testobject.testString]) {
return NO;
}
return YES;
}
- (BOOL)isEqual:(id)object { if ([self class] == [object class]) {
return [self isEqualToTestObject:(TestObject *)object];
}else {
return [super isEqual:object];
}
}
  • 有时候无需将所有数据逐个比较,只根据其中部分数据即可判明二者是否相等。

比方说一个模型类的实例是根据数据库的数据创建而来,那么其中可能会含有一个唯一标识符(unique identifier),在数据库中用作主键。这时候,我们就可以根据标识符来判定等同性,尤其是此属性声明为 readonly 时更应该如此。只要标识符相等,就可以说明这两个对象是由相同数据源创建,据此断定,其他数据也相等。

当然,只有类的编写者才知道那个关键属性是什么。

要点:不要盲目的逐个检测每条属性,而是应该按照具体需求制定检测方案

4.理解 objc_msgSend
  • 在对象上调用方法是 OC 中经常使用的方法。专业术语叫做“传递消息”,消息有名称(或叫选择子),可以接受参数,或许还有返回值。
  • 在 OC 中,对象收到消息之后,究竟该调用哪个方法完全于运行期决定,甚至可以在运行时改变,这些特性使 OC 成为一门真正的动态语言。

    给对象发送消息可以这样写:

id value = [obj messageName:parameter]

obj 叫做接收者,messageName 叫做 selector,selector 和参数合起来称为消息

编译器看到此消息后,将其转换为一条标准的 C 语言函数调用

void objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

第一个参数代表接收者,第二个代表 selector(SEL是selector类型)

这是个“参数个数可变的函数”,”…“ 代表后续参数,就是消息中的参数

  • objc_msgSend 方法在接收者所属的类中搜寻其”方法列表“,如果能找到与 selector 名称相符的方法,就跳至其实现代码。若是找不到,就沿着继承体系继续向上查找,等找到合适的方法之后再跳转。如果最终还是找不到相符的方法,就执行”消息转发“(在之后解释)。
  • 看起来,想调用一个方法似乎需要很多步骤。所幸 objc_msgSend 会将匹配结果缓存在”快速映射表“中,每个类都有这样一个缓存,若是后来还向该类发送相同的消息,那么执行起来就会很快了。
  • 其他消息发送函数
//Sends a message with a simple return value to the superclass of an instance of a class.
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
//Sends a message with a data-structure return value to an instance of a class.
objc_msgSend_stret(id _Nullable self, SEL _Nonnull op, ...)
//Sends a message with a data-structure return value to the superclass of an instance of a class.
objc_msgSendSuper_stret(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
  • 刚才提到 objc_msgSend 找到合适的方法之后,就会”跳转过去“。之所以可以这样做是因为 OC 对象的每个方法都可以视为简单的 C 函数,原型如下:
<return_type> Class_selector(id self, SEL _cmd, ...)

每个类都有一张表格,selector 的名称就是查表时所用的 key

  • 原型的样子和 objc_msgSend 很像,而且函数的最后一项操作是调用另一个函数而且不会将其返回值另作他用,就可以利用”尾调用优化“技术,令”跳至方法实现“变得简单。

尾调用技术:编译器会生成跳转至另一函数所需的指令码,而且不会向调用堆栈中推入新的”栈帧“

  • 要点:发给某对象的全部消息都要由“动态消息派发系统”来处理,该系统会查出对应的方法,并执行其代码
5.消息转发机制
  • 对象在收到无法解读的消息之后会发生什么情况?如果在控制台中看到上面这种提示信息,那就说明你给对象发送了一条其无法解读的消息,启动了消息转发机制
- 因为在运行期可以继续向类中添加方法,所以编译器在编译期还无法确知类中是否有某个方法的具体实现。
- 当对象接收到无法解读的消息,就会触发“消息转发机制”,程序员可以经由此过程告诉对象如何处理未知消息

消息转发分为两大阶段

  • 第一阶段:征询接收者,能否动态添加方法,处理当前这个“未知的 selector”
//当未知的 selector 是实例方法时的调用
+ (BOOL)resolveInstanceMethod:(SEL)sel ;
//当未知的 selector 是类方法时的调用
+ (BOOL)resolveClassMethod:(SEL)sel;

使用这种办法的前提是:相关方法的实现代码已经写好,只等着运行时加入类里面

  • 第二阶段:运行时系统会请求接收者以其他手段处理与消息相关的方法调用,可细分为两小步。
//第一步:询问能不能把未知的消息转给其他接收者处理
- (id)forwardingTargetForSelector:(SEL)aSelector ;

若当前接收者能找到备援接收者,则将其返回,若找不到,则返回 nil

-如果返回一个对象,则运行期系统把消息转给那个对象,于是消息转发结束

如果返回 nil,执行第二步

《Effective Objective-C》概念篇的更多相关文章

  1. Linux Capabilities 入门教程:概念篇

    原文链接:Linux Capabilities 入门教程:概念篇 Linux 是一种安全的操作系统,它把所有的系统权限都赋予了一个单一的 root 用户,只给普通用户保留有限的权限.root 用户拥有 ...

  2. 【转】android 电容屏(二):驱动调试之基本概念篇

    关键词:android  电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung ...

  3. 我的TDD实践---TDD概念篇

    “我的TDD实践”系列之TDD概念篇 写在前面: 我的TDD实践这几篇文章主要是围绕测试驱动开发所展开的,其中涵盖了一小部分测试理论,更多的则是关注工具的使用及环境的搭建,做到简单实践先行,后理论专精 ...

  4. DNA拷贝数变异CNV检测——基础概念篇

    DNA拷贝数变异CNV检测——基础概念篇   一.CNV 简介 拷贝数异常(copy number variations, CNVs)是属于基因组结构变异(structural variation), ...

  5. 【黑金原创教程】 FPGA那些事儿《概念篇》

    简介一本讲述非软硬片上系统的书,另外还是低级建模的使用手册. 目录[黑金原创教程] FPGA那些事儿<概念篇>:File01 - 结构的玩笑[黑金原创教程] FPGA那些事儿<概念篇 ...

  6. 常见面试题整理--Python概念篇

    希望此文可以长期更新并作为一篇Python的面试宝典.每一道题目都附有详细解答,以及更加详细的回答链接.此篇是概念篇,下一篇会更新面试题代码篇. (一).这两个参数是什么意思:*args,**kwar ...

  7. 【转帖】H5 手机 App 开发入门:概念篇

    H5 手机 App 开发入门:概念篇 http://www.ruanyifeng.com/blog/2019/12/hybrid-app-concepts.html 作者: 阮一峰 日期: 2019年 ...

  8. 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 百篇博客分析OpenHarmony源码 | v62.01

    百篇博客系列篇.本篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 51.c.h.o 本篇开始说文件系统,它是内核五大模块之一,甚至有Linux的设计哲学是" ...

  9. 鸿蒙内核源码分析(中断概念篇) | 海公公的日常工作 | 百篇博客分析OpenHarmony源码 | v43.02

    百篇博客系列篇.本篇为: v43.xx 鸿蒙内核源码分析(中断概念篇) | 海公公的日常工作 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪里 ...

随机推荐

  1. 【POJ3087】Shuffle'm Up

    本题传送门 本题知识点:宽度优先搜索 模拟 + map 本题题意有点懵.就是单纯的把S1像例子那样插到S2里,根本不是什么宽搜题,因为只是一个方向就可以了.说是搜索题倒是有点意思,因为要查重. 不过c ...

  2. Dice Similarity Coefficent vs. IoU Dice系数和IoU

    Dice Similarity Coefficent vs. IoU Several readers emailed regarding the segmentation performance of ...

  3. K8S集群搭建——基于CentOS 7系统

    环境准备集群数量此次使用3台CentOS 7系列机器,分别为7.3,7.4,7.5 节点名称 节点IPmaster 192.168.0.100node1 192.168.0.101node2 192. ...

  4. Http协议以及Httpclient

    一.HTTP之请求消息Request 客户端发送一个HTTP请求到服务器的请求消息包括以下格式: 请求行(request line).请求头部(header).空行和请求数据四个部分组成. Http请 ...

  5. 服务器负载过高问题分析-不是cpu高负载也不是IO负载如何处理(阿里 几乎是必考题)

    关于top命令 经常问load average 参考:load average 定义(网易面试) jvm dump的使用 参考:Jvm dump jstack jmap jstat 介绍与使用(内存与 ...

  6. poi导出word表格跨行

    DataCommon.java package com.ksource.pwlp.model.statistic; public class DataCommon { private Long id; ...

  7. Android Sensor 架构深入剖析【转】

    本文转载自: 1.Android sensor架构 Android4.0系统内置对传感器的支持达13种,它们分别是:加速度传感器 (accelerometer).磁力传感器(magnetic fiel ...

  8. Refused to execute script from '...' because its MIME type ('') is not executable, and strict MIME type checking is enabled.

    写在前面 部署项目到weblogic上启动首页访问空白, 浏览器控制台报如题错误. web.xml中把响应头添加防止攻击的报文过滤器禁用就行了(仅仅是为了启动), 以下为转载内容, 可以根据需要自行测 ...

  9. 微信小程序for循环中传递动态参数

    for循环中的参数,没法传到对应的 js里,所以直接在 wxml页面上跳转 发送参数的 wxml页面 <view class="uploader" wx:for=" ...

  10. UDP用于保持大量终端的在线与控制,应用与业务则通过TCP去实现。这个和FTP服务控制与数据分离,采取不同的连接,有异曲同工之处 端口映射老化时间

    移动端IM/推送系统的协议选型:UDP还是TCP? http://www.52im.net/thread-33-1-1.html