广告
返回顶部
首页 > 资讯 > 移动开发 >详解Objective-C中的语法糖@{}究竟是什么
  • 823
分享到

详解Objective-C中的语法糖@{}究竟是什么

objective-c语法糖@{} 2022-05-23 21:05:25 823人浏览 独家记忆
摘要

最近在技术群里有一个群友提出了一个问题,就是为什么下面代码打印的结果不一样? NSMutableDictionary *mDic1 = [NSMutableDictionary

最近在技术群里有一个群友提出了一个问题,就是为什么下面代码打印的结果不一样?


NSMutableDictionary *mDic1 = [NSMutableDictionary dictionaryWithDictionary:@{@"a":@1, @"a":@2}];
//'a': 1
NSMutableDictionary *mDic2 = [NSMutableDictionary dictionary];
[mDic2 setObject:@(1) forKey:@"a"];
[mDic2 setObject:@(2) forKey:@"a"];
 //'a': 2

对此,笔者稍微研究了一下,在此,我阐述一下理由并简述实验步骤

@{} 到底是什么?

造成这个数据结果的可能性之一,应该是


@{@"a":@1, @"a":@2}

本身就是一个 key 为 a, 值为 1 的字典 。

通过测试代码,如下:


NSDictionary *dic = @{@"a":@1, @"a":@2};
NSLog(@"%@", dic);

发现其本身就是一个 key 为 a, 值为 1 的字典 。

那么 @{} 到底是什么呢?其实如何操作的呢?他的分配方式究竟是什么?

实验步骤

基于网上找到的 NSDictionary 的伪代码,无论如何,当我们创建一个字典时,其最终都会执行


- (id)initWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt

那么假使我们通过 hook 监听这个方法,我们就知道在初始化时传入的 objects 和 keys 究竟是什么?但是,可惜的是,没有 hook 住。

难道是我的做法有问题吗?

笔者发现在使用 @{} 时根本就不执行这个步骤?是其他的吗?

然后笔者选择通过添加符号断点 +[NSDictionary dictionaryWithObjects:forKeys:count:] 发现,当我们赋值时,其符号断点会挂住。

我们在使用  @{} 创建字典的时候会调用这个方法吗?值得一试?

通过 hook 了字典的这个方法,我们在分类中做一个接受,当系统调用时,挂上断点


+ (id)xxx_dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt{
 for (NSUInteger i = 0; i < cnt; i++) {
 id key = keys[i];
 id obj = objects[i];
 NSLog(@"key = %@", key);
 NSLog(@"obj = %@", obj);
 }
 return [[NSDictionary class] xxx_dictionaryWithObjects:objects forKeys:keys count:cnt];
}

2021-03-30 17:13:40.971674+0800 suspenseRoad[28886:413231] key = a 2021-03-30 17:13:40.971743+0800 suspenseRoad[28886:413231] obj = 1 2021-03-30 17:13:40.971814+0800 suspenseRoad[28886:413231] key = a 2021-03-30 17:13:40.971896+0800 suspenseRoad[28886:413231] obj = 2

并等到下面的结果,我们在初始化设置的时候,传入的值都已经进入代码中,但是结果并没有

继续探究一下  +[NSDictionary dictionaryWithObjects:forKeys:count:] 的方法


+ (id)dictionaryWithDictionary:(NSDictionary *)dict
{
 size_t count = [dict count];
 id *objects = NULL;
 id *keys = NULL;

 if (count > 0) {
 objects = malloc(sizeof(id) * count);
 if (UNLIKELY(objects == NULL)) {
  return NULL;
 }
 keys = malloc(sizeof(id) * count);
 if (UNLIKELY(keys == NULL)) {
  free(objects);
  return NULL;
 }
 }
 
 [dict getObjects:objects andKeys:keys];
 id obj = [[self alloc] initWithObjects:objects forKeys:keys count:count];

 if (objects != NULL) {
 free(objects);
 }
 if (keys != NULL) {
 free(keys);
 }

 return [obj autorelease];
}

猜测

这时候可能会让改变数据的地方只有两个:


 [dict getObjects:objects andKeys:keys];
//或者
 id obj = [[self alloc] initWithObjects:objects forKeys:keys count:count];

由于上述两个问题笔者都无办法断点到,如果读者大大有办法的话,希望读者大大尝试。笔者根据两个方法的代码进行了自己的「大胆猜测」——也就是瞎猜

很可惜,都没有改掉。在代码中没看到任何一个方法可以做到对 objects 和 keys 的选择遍历。

CFBasicHashAddValue 和 CFBasicHashSetValue

看来应该不是其初始化方法的问题,然后笔者比较了字典的 setObject:forKey 的实现。发现如题的两个方法:


CF_PRIVATE Boolean CFBasicHashAddValue(CFBasicHashRef ht, uintptr_t stack_key, uintptr_t stack_value) {
 ···
 CFBasicHashBucket bkt = __CFBasicHashFindBucket(ht, stack_key);
 if (0 < bkt.count) {
  ht->bits.mutations++;
  if (ht->bits.counts_offset && bkt.count < LONG_MAX) { // if not yet as large as a CFIndex can be... otherwise clamp and do nothing
   __CFBasicHashIncSlotCount(ht, bkt.idx);
   return true;
  }
 } else {
  __CFBasicHashAddValue(ht, bkt.idx, stack_key, stack_value);
  return true;
 }
 return false;
}

CF_PRIVATE void CFBasicHashSetValue(CFBasicHashRef ht, uintptr_t stack_key, uintptr_t stack_value) {
 ···
 CFBasicHashBucket bkt = __CFBasicHashFindBucket(ht, stack_key);
 if (0 < bkt.count) {
  __CFBasicHashReplaceValue(ht, bkt.idx, stack_key, stack_value);
 } else {
  __CFBasicHashAddValue(ht, bkt.idx, stack_key, stack_value);
 }
}

感觉胜利不远了,因为__CFBasicHashReplaceValue 这个方法语义上是一个替换。那么其本质应该就是 CFBasicHashAddValue ,当存在同值的 key 的时候,并不会再次加入,并且也解释了,最终的结果是设置为前面的值而不是设置在后面的值。

同样你也可以得出下面的值了,欢迎把答案写在评论区哦。


[NSDictionary dictionaryWithObjects:@[@1,@2,@3,@4,@5,@6,@7,@8,@9,@0] forKeys:@[@"a",@"b", @"a", @"b", @"a", @"a", @"b", @"b", @"a", @"b"]]

其他

在 hook 字典本身的dictionaryWithObjects:forKeys:count: 时,我们需要谨慎断点的时间,包括当不限于系统的状态栏等信息最终都会存进一个字典中,其存入的时机就是项目运行的时候,最好在NSDictionary *dic = @{@"a":@1, @"a":@2};之前挂上断点,然后在放开dictionaryWithObjects:forKeys:count: 断点。

到此这篇关于Objective-C中的语法糖@{}究竟是什么的文章就介绍到这了,更多相关Objective-C语法糖@{}内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解Objective-C中的语法糖@{}究竟是什么

本文链接: https://www.lsjlt.com/news/30487.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解Objective-C中的语法糖@{}究竟是什么
    最近在技术群里有一个群友提出了一个问题,就是为什么下面代码打印的结果不一样? NSMutableDictionary *mDic1 = [NSMutableDictionary ...
    99+
    2022-05-23
    objective-c 语法糖 @{}
  • 详解C#中检查null的语法糖
    目录函数参数null检查传统写法ThrowIfNullC# 11的!!语法(已经取消)有关null的一些操作=.[]一些操作结语今天看到已经更新了devblogs,新增的C# 11的...
    99+
    2022-11-13
  • Go语言中的逃逸分析究竟是什么?
    目录1、逃逸分析介绍2、Go中内存分配在哪里?3、Go与C++内存分配的区别4、逃逸分析骚操作5、逃逸分析引申示例说明 1、逃逸分析介绍 学计算机的同学都知道,在编译原理中,分析指针...
    99+
    2022-11-12
  • 传说中VUE的语法糖到底是做什么的
    目录一、什么是语法糖?二、VUE中语法糖有哪些?1、最常见的语法糖 v-model2、v-bind 的语法糖3、v-on 的语法糖4、修饰符5、动态css6、注册组件语法糖一、什么是...
    99+
    2022-11-12
  • c语言中register的用法是什么
    在C语言中,`register`是一个存储类,用于向编译器建议将变量存储在寄存器中,以提高访问速度。然而,现代的编译器通常会自动优化...
    99+
    2023-09-16
    c语言 register
  • c语言中int的用法是什么
    c语言中int的用法是声明变量、函数参数和返回值、数组和位操作。1、声明变量,使用int关键字可以声明一个整数变量。变量是在程序中用来存储数据的一种标识符;2、函数参数和返回值,函数是一段可重用的代码块,用于执行特定的任务;3、数组,数组是...
    99+
    2023-08-09
  • c语言中malloc的用法是什么
    malloc函数是C语言中动态内存分配的函数,用于在堆区中分配一块指定大小的内存空间,并返回该内存空间的首地址。malloc的用法如...
    99+
    2023-09-20
    c语言
  • c语言中bit的用法是什么
    在C语言中,bit是计算机中的最小存储单位,它代表了二进制的0或1。C语言中的bit操作主要用于对二进制位进行位操作,如位与、位或、...
    99+
    2023-08-19
    c语言
  • C#中的变量定义语法是什么
    这篇文章主要介绍“C#中的变量定义语法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C#中的变量定义语法是什么”文章能帮助大家解决问题。C# 中的变量定义C# 中变量定义的语法:<dat...
    99+
    2023-06-17
  • C#中声明数组的语法是什么
    这篇文章主要介绍“C#中声明数组的语法是什么”,在日常操作中,相信很多人在C#中声明数组的语法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#中声明数组的语法是什么”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-17
  • C语言中power函数的用法是什么
    在C语言中,power函数用于计算一个数的幂。函数原型为:```cdouble pow(double x, double y);``...
    99+
    2023-10-12
    C语言
  • c语言中fopen函数的用法是什么
    fopen函数是C语言中用于打开文件的函数。其使用方法如下:```cFILE *fopen(const char *filename...
    99+
    2023-09-12
    C语言
  • c语言中prime函数的用法是什么
    在C语言中,prime函数的用法是判断一个数是否为素数(质数)。素数是指除了1和它本身之外没有其他因子的正整数。例如,2、3、5、7...
    99+
    2023-09-27
    c语言
  • c语言中break的使用方法是什么
    在C语言中,break语句用于终止循环或switch语句,并将程序控制流转移到循环或switch语句之后的下一行代码。break的使...
    99+
    2023-10-12
    c语言
  • C++中CLI模块的基本语法是什么
    本文小编为大家详细介绍“C++中CLI模块的基本语法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++中CLI模块的基本语法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。托管对象的创建和引用在前文...
    99+
    2023-07-02
  • C语言编程中if的用法是什么
    C语言编程中if的用法是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。顺序,选择和循环是程序的三大基本结构,无论多么复杂的程序,都是用这三种结构写出来的。i...
    99+
    2023-06-03
  • C语言中strtol函数的用法是什么
    这篇文章主要讲解了“C语言中strtol函数的用法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言中strtol函数的用法是什么”吧!strtol 函数用法strtol是一个C语言...
    99+
    2023-06-20
  • C/C++中for语句循环使用的方法是什么
    这篇文章主要介绍“C/C++中for语句循环使用的方法是什么”,在日常操作中,相信很多人在C/C++中for语句循环使用的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C/C++中for语句循环使用...
    99+
    2023-07-05
  • c语言内存泄漏的解决方法是什么
    在C语言中,内存泄漏是指在动态内存分配后,没有释放该内存导致内存空间无法被再次使用的情况。解决内存泄漏的方法可以包括以下几个方面:1...
    99+
    2023-09-27
    c语言
  • c语言中while语句的用法及规则是什么
    在C语言中,while语句用于创建一个循环,只要指定的条件为真,就会重复执行特定的代码块。while语句的一般格式如下:```whi...
    99+
    2023-09-15
    c语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作