iOS里面的多级指针

最近面试,碰到一个好玩的公司,出了一个面试题,ta给我了一张纸,让写出NSArray的enumerateObjectsUsingBlock内部怎么实现的。

    NSArray * list = @[@"1",@"2",@"3"];
    [list enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if([obj isEqualToString:@"2"]){
           *stop = YES;
        }
    }];

这题目漂漂亮亮的写出来,起码需要以下要求:
1、对block有点理解,要不然写不出block,哈哈。
2、对指针有点理解,要不然写不出来。
3、对自己足够自信,对苹果sdk内部实现不能有恐惧。

block晚点再说,先看看指针。

1、指针基础:p、*p和&p三者的区别

指针四元素:

指针的类型

你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型

指针所指向的类型

把指针声明语句中的指针名字和名字左边的指针声明符去掉,剩下的就是指针所指向的类型*

指针的值

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。

指针本身所占据的内存区

指针本身占了多大的内存?用函数sizeof(指针的类型)测一下就知道了,不同位数的机器大小不同

 NSInteger aaa = 3;
 NSLog(@"aaa的内存地址====%p",&aaa); //aaa的内存地址0x111

 NSInteger *bbb = &aaa;
 NSLog(@"bbb变量存的内容====%p",bbb); //这获取的就是示意图中的0x111
 NSLog(@"bbb的内存地址====%p",&bbb); //这获取的就是示意图中的0x222
变量内存示意.png

bbb是一个指针变量的名字,表示此指针变量指向的内存地址,如果使用%p来输出的话,它将是一个16进制数,从上面的结果可以看到打印bbb和&aaa的值是一样

*bbb表示此指针指向的内存地址中存放的内容,一般是一个和指针类型一致的变量或者常量
&是取地址运算符,&bbb就是取指针bbb的地址;

&bbb和bbb的区别在于:指针bbb同时也是个变量,既然是变量,编译器肯定要为其分配内存地址,&bbb就表示编译器为变量bbb分配的内存地址;而因为bbb是一个指针变量,这种特殊的身份注定了它要指向另外一个内存地址,程序员按照程序的需要让它指向一个内存地址,所以bbb表示它指向的内存地址。

2、基本数据类型、对象类型

    char a = 10;
    char *p = &a;
    char value = *p;
    printf("value的值:%d", value);  //输出结果:value的值:10
    NSString *name = @"solo";
    NSLog(@"xxxx的内存地址====%p",name); //下图中solo的内存首地址,也是name指针在内存中存的内容
    NSLog(@"name的内存地址===%p",&name); //name指针的内存地址
    NSLog(@"name的description===%@",name); //name的description

对象类型,内存分布复杂,结构体是一片内存区域。


示意图1.png

NSString本身也是一个对象,它不止是char *这些基本类型这么简单。本质上OC的对象是一个结构体,是一片内存区域,我们并没有方法能直接完整打印出这个结构体。NSLog遇到%@格式和接收对象作为参数时,直接调用的是对象的description方法。这里与基本数据类型的处理是有区别的。

3、iOS中多级指针的应用

指针在iOS中运用十分广泛,只是太频繁没意识到而已,其实每个实例对象都是指针。这里说的应用是指多级指针的运用。下面这俩货我面写代码会经常看到:

    NSArray * list = @[@"1",@"2",@"3"];
    [list enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"enumerateObjectsUsingBlock===%@",obj);
        if([obj isEqualToString:@"2"]){
           *stop = YES;
        }
    }];
    NSError *error = nil;
    [[NSFileManager defaultManager] moveItemAtPath:@"" toPath:@"" error:& error];
    if (error) {
       NSLog(@"move failed:%@", [error localizedDescription]);
    }

为什么这么写能修改函数外面的值?换个有函数实现简单的栗子:

- (void)testBaseData
{
    NSInteger aaa = 3;
    NSLog(@"函数前===%p",&aaa);//函数前===0x7ffeec80fba8
    [self getNewCount:&aaa];
    NSLog(@"aaa2===%ld",aaa);//aaa2===200
}

-(void)getNewCount:(NSInteger *)countaa
{
    NSLog(@"函数里===%p",countaa);//函数里===0x7ffeec80fba8
    *countaa = 200;
}

基本数据类型,会显得比较简单。可以看出函数前和函数里指针一样。
把aaa的内存地址,赋值给了指针countaa,countaa就是函数外面的aaa。修改countaa的值,就是修改aaa变量在内存中存的值。

- (void)showError
{
    NSError *error = nil;
    NSLog(@"函数前==%p", &error);//函数前==0x7ffee06dfd38
    [self handleResponseCode:0 error:&error];
    NSLog(@"函数后值==%@", error);//函数后值==Error Domain=NSCocoaErrorDomain Code=0 "(null)" UserInfo={code=0}
}

- (void)handleResponseCode:(NSInteger)code error:(NSError **)err
{
    NSLog(@"函数里==%p", err);//函数里==0x7ffee06dfd30
    if (code == 0) {
        *err = [NSError errorWithDomain:NSCocoaErrorDomain code:code userInfo:@{@"code":@(code)}];
    }
}

对着下面的内存示意图分析下:

示意图.png

error是个指针,开始指向nil,在调用下面的函数的时候,通过取地符&,把error的内存地址(0x222)赋值给了新的指针变量err。
在函数中,*err就是err指针指向的变量(就是函数外面的error)。修改 *err的指针指向就是修改函数外error的指针指向。
注意:这里函数外的内存地址0x7ffee06dfd38和函数里面0x7ffee06dfd30会有略微不同,是__autoreleasing搞的鬼,暂且忽略,具体看这个吧

结论:我们通过一个指针参数作为桥梁,成功修改了函数外面变量的值。

这么写到底有啥好处?谁也不会没事撑的,搞这么个幺蛾子。

看个栗子:

- (void)testManyParameter
{
    NSString * name = @"solo";
    NSInteger  age = 18;
    NSString * money = @"100W";
    BOOL isRichGuy = YES;
    CGFloat degress = 0.55;

    [self fucNewName:&name age:&age money:&money isRichGuy:&isRichGuy degress:&degress];
    NSLog(@"name====%@",name);
}

-(void)fucNewName:(NSString **)newName
              age:(NSInteger *)age
            money:(NSString **)money
        isRichGuy:(BOOL *)isRichGuy
          degress:(CGFloat *)degress
{
    *newName = @"fuck";
    *age = 20;
    *money = @"2000";
    *isRichGuy = NO;
    *degress = 0.66;
}

看出来了吧,返回参数可以不用放容器里返回,完全可以直接修改函数外面的值,也不用担心NSInteger这些变量不能直接放进容器里面的问题。

4、空指针nil、野指针、僵尸对象

1.空指针值为nil,没有指向。由于iOS中采用的是对调用者发消息,如果消息的接受者为nil,对空指针发任何消息不起任何作用。

    NSObject * object = nil;
    NSLog(@"函数==%p", &object);//函数==0x7ffeeaba0ba8
    NSLog(@"函数==%p", object);//函数==0x0
示意图.png

这里object存储的是0x0,表示object是一个空指针,空指针也是指针,也有四元素。

2.野指针不是nil指针,是指向"垃圾"内存(不可用内存)的指针。当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称野指针。如用assign修饰对象就出现导致野指针,因为对象创建出来没有任何强指针指向它,所以创建完以后立即会被释放了,这时候指针指向的地方已经不可用,所以就成了野指针。
3.僵尸对象,在OC中,对象被释放后所占用的内存在没有被复写(重新分配给其他对象)前称为僵尸对象,这是野指针是可以访问该内存的,因为对象的数据还在,所以程序不会报错。但是该内存一旦重新分配给其他对象就会出现问题。

最后给出最开始题目的答案: NSArray的enumerateObjectsUsingBlock实现类似下面:

- (void) enumTestBlock:(void (^)(id obj, NSUInteger index, BOOL * stop))enumBlock {
    BOOL stopNow = NO;
    for (int index = 0; index < self.count; index++ ) {
        if (!stopNow) {
            enumBlock(self[index], index, &stopNow);
        } else {
            break;
        }
    }
}

一些自己的理解记录下来,希望没有不对的地方。
参考1://www.greatytc.com/p/5b2c7bbc32d6
参考2://www.greatytc.com/p/c58e089ba219
参考3:https://blog.csdn.net/wnnvv/article/details/81144219

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353