iOS里面的多級指針

最近面試婚瓜,碰到一個好玩的公司沼死,出了一個面試題,ta給我了一張紙倘潜,讓寫出NSArray的enumerateObjectsUsingBlock內(nèi)部怎么實現(xiàn)的。

    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內(nèi)部實現(xiàn)不能有恐懼钥组。

block晚點再說输硝,先看看指針。

1程梦、指針基礎(chǔ):p点把、*p和&p三者的區(qū)別

指針四元素:

指針的類型

你只要把指針聲明語句里的指針名字去掉,剩下的部分就是這個指針的類型

指針所指向的類型

把指針聲明語句中的指針名字和名字左邊的指針聲明符去掉屿附,剩下的就是指針所指向的類型*

指針的值

指針的值是指針本身存儲的數(shù)值郎逃,這個值將被編譯器當作一個地址,而不是一個一般的數(shù)值挺份。

指針本身所占據(jù)的內(nèi)存區(qū)

指針本身占了多大的內(nèi)存褒翰?用函數(shù)sizeof(指針的類型)測一下就知道了,不同位數(shù)的機器大小不同

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

 NSInteger *bbb = &aaa;
 NSLog(@"bbb變量存的內(nèi)容====%p",bbb); //這獲取的就是示意圖中的0x111
 NSLog(@"bbb的內(nèi)存地址====%p",&bbb); //這獲取的就是示意圖中的0x222
變量內(nèi)存示意.png

bbb是一個指針變量的名字匀泊,表示此指針變量指向的內(nèi)存地址优训,如果使用%p來輸出的話,它將是一個16進制數(shù)各聘,從上面的結(jié)果可以看到打印bbb和&aaa的值是一樣

*bbb表示此指針指向的內(nèi)存地址中存放的內(nèi)容揣非,一般是一個和指針類型一致的變量或者常量
&是取地址運算符,&bbb就是取指針bbb的地址躲因;

&bbb和bbb的區(qū)別在于:指針bbb同時也是個變量早敬,既然是變量,編譯器肯定要為其分配內(nèi)存地址大脉,&bbb就表示編譯器為變量bbb分配的內(nèi)存地址搞监;而因為bbb是一個指針變量,這種特殊的身份注定了它要指向另外一個內(nèi)存地址镰矿,程序員按照程序的需要讓它指向一個內(nèi)存地址琐驴,所以bbb表示它指向的內(nèi)存地址。

2、基本數(shù)據(jù)類型棍矛、對象類型

    char a = 10;
    char *p = &a;
    char value = *p;
    printf("value的值:%d", value);  //輸出結(jié)果:value的值:10
    NSString *name = @"solo";
    NSLog(@"xxxx的內(nèi)存地址====%p",name); //下圖中solo的內(nèi)存首地址安疗,也是name指針在內(nèi)存中存的內(nèi)容
    NSLog(@"name的內(nèi)存地址===%p",&name); //name指針的內(nèi)存地址
    NSLog(@"name的description===%@",name); //name的description

對象類型,內(nèi)存分布復雜,結(jié)構(gòu)體是一片內(nèi)存區(qū)域够委。


示意圖1.png

NSString本身也是一個對象荐类,它不止是char *這些基本類型這么簡單。本質(zhì)上OC的對象是一個結(jié)構(gòu)體,是一片內(nèi)存區(qū)域,我們并沒有方法能直接完整打印出這個結(jié)構(gòu)體茁帽。NSLog遇到%@格式和接收對象作為參數(shù)時玉罐,直接調(diào)用的是對象的description方法。這里與基本數(shù)據(jù)類型的處理是有區(qū)別的潘拨。

3吊输、iOS中多級指針的應(yīng)用

指針在iOS中運用十分廣泛,只是太頻繁沒意識到而已铁追,其實每個實例對象都是指針季蚂。這里說的應(yīng)用是指多級指針的運用。下面這倆貨我面寫代碼會經(jīng)忱攀看到:

    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]);
    }

為什么這么寫能修改函數(shù)外面的值扭屁?換個有函數(shù)實現(xiàn)簡單的栗子:

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

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

基本數(shù)據(jù)類型,會顯得比較簡單涩禀×侠模可以看出函數(shù)前和函數(shù)里指針一樣。
把aaa的內(nèi)存地址艾船,賦值給了指針countaa葵腹,countaa就是函數(shù)外面的aaa。修改countaa的值屿岂,就是修改aaa變量在內(nèi)存中存的值践宴。

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

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

對著下面的內(nèi)存示意圖分析下:

示意圖.png

error是個指針,開始指向nil,在調(diào)用下面的函數(shù)的時候,通過取地符&爷怀,把error的內(nèi)存地址(0x222)賦值給了新的指針變量err浴井。
在函數(shù)中,*err就是err指針指向的變量(就是函數(shù)外面的error)霉撵。修改 *err的指針指向就是修改函數(shù)外error的指針指向。
注意:這里函數(shù)外的內(nèi)存地址0x7ffee06dfd38和函數(shù)里面0x7ffee06dfd30會有略微不同洪囤,是__autoreleasing搞的鬼徒坡,暫且忽略,具體看這個吧

結(jié)論:我們通過一個指針參數(shù)作為橋梁瘤缩,成功修改了函數(shù)外面變量的值喇完。

這么寫到底有啥好處?誰也不會沒事?lián)蔚陌。氵@么個幺蛾子锦溪。

看個栗子:

- (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;
}

看出來了吧不脯,返回參數(shù)可以不用放容器里返回,完全可以直接修改函數(shù)外面的值,也不用擔心NSInteger這些變量不能直接放進容器里面的問題刻诊。

4防楷、空指針nil、野指針则涯、僵尸對象

1.空指針值為nil复局,沒有指向。由于iOS中采用的是對調(diào)用者發(fā)消息粟判,如果消息的接受者為nil亿昏,對空指針發(fā)任何消息不起任何作用。

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

這里object存儲的是0x0,表示object是一個空指針,空指針也是指針档礁,也有四元素角钩。

2.野指針不是nil指針,是指向"垃圾"內(nèi)存(不可用內(nèi)存)的指針呻澜。當所指向的對象被釋放或者收回递礼,但是對該指針沒有作任何的修改,以至于該指針仍舊指向已經(jīng)回收的內(nèi)存地址易迹,此情況下該指針便稱野指針宰衙。如用assign修飾對象就出現(xiàn)導致野指針,因為對象創(chuàng)建出來沒有任何強指針指向它睹欲,所以創(chuàng)建完以后立即會被釋放了供炼,這時候指針指向的地方已經(jīng)不可用,所以就成了野指針窘疮。
3.僵尸對象,在OC中袋哼,對象被釋放后所占用的內(nèi)存在沒有被復寫(重新分配給其他對象)前稱為僵尸對象,這是野指針是可以訪問該內(nèi)存的闸衫,因為對象的數(shù)據(jù)還在涛贯,所以程序不會報錯。但是該內(nèi)存一旦重新分配給其他對象就會出現(xiàn)問題蔚出。

最后給出最開始題目的答案: NSArray的enumerateObjectsUsingBlock實現(xiàn)類似下面:

- (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:http://www.reibang.com/p/5b2c7bbc32d6
參考2:http://www.reibang.com/p/c58e089ba219
參考3:https://blog.csdn.net/wnnvv/article/details/81144219

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骄酗,一起剝皮案震驚了整個濱河市稀余,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趋翻,老刑警劉巖睛琳,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡师骗,警方通過查閱死者的電腦和手機历等,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辟癌,“玉大人寒屯,你說我怎么就攤上這事≡复” “怎么了浩螺?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仍侥。 經(jīng)常有香客問我要出,道長,這世上最難降的妖魔是什么农渊? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任患蹂,我火速辦了婚禮,結(jié)果婚禮上砸紊,老公的妹妹穿的比我還像新娘传于。我一直安慰自己,他們只是感情好醉顽,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布沼溜。 她就那樣靜靜地躺著,像睡著了一般游添。 火紅的嫁衣襯著肌膚如雪系草。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天唆涝,我揣著相機與錄音找都,去河邊找鬼。 笑死廊酣,一個胖子當著我的面吹牛能耻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亡驰,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼晓猛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凡辱?” 一聲冷哼從身側(cè)響起鞍帝,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎煞茫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡续徽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年蚓曼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钦扭。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡纫版,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出客情,到底是詐尸還是另有隱情其弊,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布膀斋,位于F島的核電站梭伐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏仰担。R本人自食惡果不足惜糊识,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摔蓝。 院中可真熱鬧赂苗,春花似錦、人聲如沸贮尉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猜谚。三九已至败砂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間龄毡,已是汗流浹背吠卷。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沦零,地道東北人祭隔。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像路操,于是被迫代替她去往敵國和親疾渴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355