Objective-C 非主流代碼

我是前言

看開源代碼時雳攘,總會看到一些大神級別的代碼再层,給人眼前一亮的感覺畔裕,多數(shù)都是被淡忘的C語言語法昧狮,總結(jié)下objc寫碼中遇到的各類非主流
代碼技巧和一些妙用:

  • [娛樂向]objc最短的方法聲明
  • [C]結(jié)構(gòu)體的初始化
  • [C]三元條件表達式的兩元使用
  • [C]數(shù)組的下標初始化
  • [objc]可變參數(shù)類型的block
  • [objc]readonly屬性支持擴展的寫法
  • [C]小括號內(nèi)聯(lián)復合表達式
  • [娛樂向]奇葩的C函數(shù)寫法
  • [Macro]預處理時計算可變參數(shù)個數(shù)
  • [Macro]預處理斷言
  • [多重]帶自動提示的keypath宏

[娛樂向]objc最短的方法聲明

先來個娛樂向的。方法聲明時有一下幾個trick:
返回值的- (TYPE)如果不寫括號萧诫,編譯器默認認為是- (id)類型:

- init;
- (id)init; // 等價于

同理斥难,參數(shù)如果不寫類型默認也是id類型:

- (void)foo:arg;
- (void)foo:(id)arg; // 等價于

還有,有多參數(shù)時方法名參數(shù)提示語可以為空

- (void):(id)arg1 :(id)arg2;
- (void)foo:(id)arg1 bar:(id)arg2; // 省略前

綜上帘饶,最短的函數(shù)可以寫成這樣:

- _; // 沒錯哑诊,這是一個oc方法聲明
- :_; // 這是一個帶一個參數(shù)的oc方法聲明
// 等價于
- (id)_;
- (id) :(id)_;

PS: 方法名都沒的方法只能靠performSelector來調(diào)用了,selector":"


<a id="結(jié)構(gòu)體初始化"></a>[C]結(jié)構(gòu)體的初始化

// 不加(CGRect)強轉(zhuǎn)也不會warning
CGRect rect1 = {1, 2, 3, 4};
CGRect rect2 = {.origin.x=5, .size={10, 10}}; // {5, 0, 10, 10}
CGRect rect3 = {1, 2}; // {1, 2, 0, 0}

[C]三元條件表達式的兩元使用

三元條件表達式?:是C中唯一一個三目運算符及刻,用來替代簡單的if-else語句镀裤,同時也是可以兩元使用的:

NSString *string = inputString ?: @"default";
NSString *string = inputString ? inputString : @"default"; // 等價

[C]數(shù)組的下標初始化

const int numbers[] = {
 [1] = 3,
 [2] = 2,
 [3] = 1,
 [5] = 12306
};
// {0, 3, 2, 1, 0, 12306}

這個特性可以用來做枚舉值和字符串的映射

typedef NS_ENUM(NSInteger, XXType){ 
          XXType1, 
          XXType2
};
const NSString *XXTypeNameMapping[] = { 
         [XXType1] = @"Type1", 
         [XXType2] = @"Type2"
};

[objc]可變參數(shù)類型的block

一個block像下面一樣聲明:

void(^block1)(void);
void(^block2)(int a);
void(^block3)(NSNumber *a, NSString *b);

如果block的參數(shù)列表為空的話,相當于可變參數(shù)(不是void)

void(^block)(); // 返回值為void缴饭,參數(shù)可變的block
block = block1; // 正常
block = block2; // 正常
block = block3; // 正常
block(@1, @"string"); // 對應上面的block3
block(@1); // block3的第一個參數(shù)為@1暑劝,第二個為nil

這樣,block的主調(diào)和回調(diào)之間可以通過約定來決定block回傳回來的參數(shù)是什么茴扁,有幾個铃岔。如一個對網(wǎng)絡(luò)層的調(diào)用:

- (void)requestDataWithApi:(NSInteger)api block:(void(^)())block
{ 
        if (api == 0) {
               block(1, 2);
       }  else if (api == 1) { 
              block(@"1", @2, @[@"3", @"4", @"5"]); 
      }
}

主調(diào)者知道自己請求的是哪個Api汪疮,那么根據(jù)約定峭火,他就知道block里面應該接受哪幾個參數(shù):

[server requestDataWithApi:0 block:^(NSInteger a, NSInteger b){ 
          // ...
}];
[server requestDataWithApi:1 block:^(NSString *s, NSNumber *n, NSArray *a){
         // ...
}];

這個特性在Reactive Cocoa
-combineLatest:reduce:等類似方法中已經(jīng)使用的相當好了毁习。

+ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock;

[objc]readonly屬性支持擴展的寫法

假如一個類有一個readonly屬性:

@interface Sark : NSObject
@property (nonatomic, readonly) NSArray *friends;
@end

.m中可以使用_friends來使用自動合成的這個變量,但假如:

  • 習慣使用self.來set實例變量時(只合成了getter)
  • 希望重寫getter進行懶加載時(重寫getter時則不會生成下劃線的變量,除非手動@synthesize
  • 允許子類重載這個屬性來修改它時(編譯報錯屬性修飾符不匹配)

這種readonly聲明方法就行不通了卖丸,所以下面的寫法更有通用性:

@interface Sark : NSObject
@property (nonatomic, readonly, copy/*加上setter屬性修飾符*/) NSArray *friends;
@end

如想在.m中像正常屬性一樣使用:

@interface Sark ()
@property (nonatomic, copy) NSArray *friends;
@end

子類化時同理纺且。iOS SDK中很多地方都用到了這個特性。


[C]小括號內(nèi)聯(lián)復合表達式

A compound statement enclosed in parentheses原諒我的渣翻譯- -稍浆,來自《gcc官方對此的說明》载碌,源自gcc對c的擴展,如今被clang繼承衅枫。

RETURN_VALUE_RECEIVER = {(
// Do whatever you want
RETURN_VALUE; // 返回值
)};

于是乎可以發(fā)揮想象力了:

self.backgroundView = ({
UIView *view = [[UIView alloc] initWithFrame:self.view.bounds];
view.backgroundColor = [UIColor redColor];
view.alpha = 0.8f;view;
});

有點像block和內(nèi)聯(lián)函數(shù)的結(jié)合體嫁艇,它最大的意義在于將代碼整理分塊,將同一個邏輯層級的代碼包在一起弦撩;同時對于一個無需復用小段邏輯步咪,也免去了重量級的調(diào)用函數(shù),如:

self.result = ({
double result = 0;
for (int i = 0; i <= M_2_PI; i+= M_PI_4) {
result += sin(i);
}
result;
});

這樣使得代碼量增大時層次仍然能比較明確益楼。

PS: 返回值和代碼塊結(jié)束點必須在結(jié)尾


[娛樂向]奇葩的C函數(shù)寫法

正常編譯執(zhí)行:

int sum(a,b)
int a; int b;
{
return a + b;
}

[Macro]預處理時計算可變參數(shù)個數(shù)

#define COUNT_PARMS2(_a1, _a2, _a3, _a4, _a5, RESULT, …) RESULT
#define COUNT_PARMS(…) COUNT_PARMS2(**VA_ARGS**, 5, 4, 3, 2, 1)
int count = COUNT_PARMS(1,2,3); // 預處理時count==3

[Macro]預處理斷言

下面的斷言在編譯前就生效

#define C_ASSERT(test) \
switch(0) {\
case 0:\
case test:;\
}

如斷言上面預處理時計算可變參數(shù)個數(shù):

C_ASSERT(COUNT_PARMS(1,2,3) == 2);

如果斷言失敗猾漫,相當于switch-case中出現(xiàn)了兩個case:0,則編譯報錯感凤。


[多重]帶自動提示的keypath宏

源自Reactive Cocoa中的宏:

#define keypath2(OBJ, PATH) \
(((void)(NO && ((void)OBJ.PATH, NO)), # PATH))

原來寫過一篇《介紹RAC宏的文章》中曾經(jīng)寫過悯周。這個宏在寫PATH參數(shù)的同時是帶自動提示的:


逗號表達式

逗號表達式取后值,但前值的表達式參與運算陪竿,可用void忽略編譯器警告

int a = ((void)(1+2), 2); // a == 2

于是上面的keypath宏的輸出結(jié)果是#PATH也就是一個c字符串


邏輯最短路徑

之前的文章沒有弄清上面宏中NO&&NO的含義禽翼,其實這用到了編譯器優(yōu)化的特性:

if (NO && [self shouldDo]/*不執(zhí)行*/) {
// 不執(zhí)行
}

編譯器知道在NO后且什么的結(jié)果都是NO,于是后面的語句被優(yōu)化掉了萨惑。也就是說keypath宏中這個NO && ((void)OBJ.PATH, NO)
就使得在編譯后后面的部分不出現(xiàn)在最后的代碼中捐康,于是乎既實現(xiàn)了keypath的自動提示功能,又保證編譯后不執(zhí)行多余的代碼庸蔼。


References

https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html


原創(chuàng)文章解总,轉(zhuǎn)載請注明原地址:blog.sunnyxx.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市姐仅,隨后出現(xiàn)的幾起案子花枫,更是在濱河造成了極大的恐慌,老刑警劉巖掏膏,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劳翰,死亡現(xiàn)場離奇詭異,居然都是意外死亡馒疹,警方通過查閱死者的電腦和手機佳簸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人生均,你說我怎么就攤上這事听想。” “怎么了马胧?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵汉买,是天一觀的道長。 經(jīng)常有香客問我佩脊,道長蛙粘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任威彰,我火速辦了婚禮出牧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歇盼。我一直安慰自己崔列,他們只是感情好,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布旺遮。 她就那樣靜靜地躺著赵讯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耿眉。 梳的紋絲不亂的頭發(fā)上边翼,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音鸣剪,去河邊找鬼组底。 笑死,一個胖子當著我的面吹牛筐骇,可吹牛的內(nèi)容都是我干的债鸡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铛纬,長吁一口氣:“原來是場噩夢啊……” “哼厌均!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起告唆,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棺弊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后擒悬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體模她,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年懂牧,在試婚紗的時候發(fā)現(xiàn)自己被綠了侈净。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖畜侦,靈堂內(nèi)的尸體忽然破棺而出运怖,到底是詐尸還是另有隱情,我是刑警寧澤夏伊,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吻氧,受9級特大地震影響溺忧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盯孙,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一鲁森、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧振惰,春花似錦歌溉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至桶蛔,卻和暖如春匙头,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仔雷。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工蹂析, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碟婆。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓电抚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親竖共。 傳聞我的和親對象是個殘疾皇子蝙叛,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容