iOS斷言的學(xué)習(xí)

說在前面的話

什么叫斷言?你會(huì)想到這個(gè)東西NSAsert.但是,不好意思.今天我所要說的不是這個(gè).這個(gè)是系統(tǒng)的,即將學(xué)習(xí)的是自定義的斷言.強(qiáng)烈建議:先把系統(tǒng)的NSAsert弄明白了,再繼續(xù)...

  • copy下面的Code,然后運(yùn)行:
// NSAssert是一個(gè)宏定義
NSAssert(NO, @"哎呀呀,這是怎么了,怎么這么不小心呢...");
  • 然后熟悉又該死的日志出現(xiàn)了:


    哦,我明白了
  • 再將copy的那段code的'NO',換成'YES',在運(yùn)行.

  • 小總結(jié):終于明白NSAssert是干什么的(≧▽≦)/啦啦啦,終于可以自定義斷言了.系統(tǒng)的斷言最可恨了,不成立竟然直接程序崩潰,這不是我想要的效果.

自定義斷言前序

初識(shí)NSError

  • copy下面的code,然后運(yùn)行:
NSError* error = [NSError errorWithDomain:@"HG, Error!" code:205 userInfo:@{@"errorKey":@"errorValue"}];
    HGLog(@"%@", error);
  • 看結(jié)果如下:
有道理,好像我又知道了點(diǎn)啥.
  • 小總結(jié):很多時(shí)候,都見過NSError,但是......,你懂的!現(xiàn)在應(yīng)該對(duì)NSError有初步的認(rèn)識(shí)了

初識(shí)runtime

在OC中,什么是runtime是什么?簡(jiǎn)單的說就是運(yùn)行時(shí).那什么是運(yùn)行時(shí)呢?總的來說就是:OC沒有runtime,就沒有OC.看似一句廢話,其實(shí)不是廢話.接下來,我直接給出即將需要的一段runtime代碼.

  • 創(chuàng)建一個(gè)NSObject的分類HGAssert,在.h文件中,代碼如下:
.h文件
#import <Foundation/Foundation.h>

@interface NSObject (HGAssert)

+ (NSError *)hg_error;
+ (void)setHg_error:(NSError *)error;

@end

到這里,你會(huì)很驚訝的發(fā)現(xiàn),盡然是一對(duì)setter與getter方法.但是仔細(xì)想想又不對(duì)了.因?yàn)橐话闱闆r一對(duì)setter與getter方法是要有一個(gè)實(shí)例關(guān)聯(lián)的,但是這是一個(gè)分類.學(xué)過OC的小盆友都知道,分類中是不能有實(shí)例的!對(duì),你太聰明了.先來看看這個(gè)分類里面的.m文件是怎么實(shí)現(xiàn)的吧.

  • 分類HGAssert中.m代碼如下:
#import "NSObject+HGAssert.h"
#import <objc/runtime.h>

@implementation NSObject (HGAssert)

#pragma mark - 利用運(yùn)行時(shí),間接的在分類中添加實(shí)例.HGErrorKey將作為一種關(guān)聯(lián)
static const char HGErrorKey = '\0';
+ (NSError *)hg_error
{
    return objc_getAssociatedObject(self, &HGErrorKey);
}

+ (void)setHg_error:(NSError *)error
{
    objc_setAssociatedObject(self, &HGErrorKey, error, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

在這段代碼中,有三個(gè)東西可能你有點(diǎn)陌生:1.#import <objc/runtime.h>,2.objc_getAssociatedObject,3.objc_setAssociatedObject.這就是runtime中的核心技術(shù)之一.詳細(xì)的內(nèi)容請(qǐng)查看官方文檔學(xué)習(xí).

  • setter方法與getter方法的作用就是:在setter方法中設(shè)置一個(gè)值,用getter方法來獲取.只是在普通的類中是通過實(shí)例將setter與getter方法來關(guān)聯(lián)的.在分類中如果也想要實(shí)現(xiàn)對(duì)應(yīng)的setter與getter方法相關(guān)聯(lián),就要利用runtime技術(shù).使用一個(gè)HGErrorKey類關(guān)聯(lián).
  • 開心了吧,終于知道怎么在一個(gè)分類中關(guān)聯(lián)一個(gè)getter與setter方法了吧.
作業(yè):通過上述解說,參照如下代碼,請(qǐng)找出異同,并思考其用處:
.h文件
#import <UIKit/UIKit.h>

@interface UIView (HG)

@property (nonatomic, assign) CGFloat x;

@end


.m文件

#import "UIView+HG.h"

@implementation UIView (HG)

- (void)setX:(CGFloat)x
{
    CGRect frame = self.frame;
    frame.origin.x = x;
    self.frame = frame;
}

- (CGFloat)x
{
    return self.frame.origin.x;
}

@end

  • 這里的@property語法與普通類中的@property語法有何異同?
  • 請(qǐng)思考這個(gè)分類的用途,并補(bǔ)全這個(gè)分類的其它功能(y, centerX, centerY, width, height, size, origin ,......)
驗(yàn)收
  • 感謝網(wǎng)友@zy30651提醒,我沒有將下面用到的類(HGAssertMode)貼出來.此類代碼如下:
.h文件
#import <Foundation/Foundation.h>

@interface HGAssertMode : NSObject

@end

.m文件
#import "HGAssertMode.h"

@implementation HGAssertMode

@end

這個(gè)類主要的用途是:驗(yàn)證上面的繼承于NSObject的分類(HGAssert)中的兩個(gè)方法(setHg_error:與hg_error).

  • 請(qǐng)看代碼
NSError* error = [NSError errorWithDomain:@"HG, Error!" code:205 userInfo:@{@"errorKey":@"errorValue"}];
//    HGLog(@"%@", error);
    
[HGAssertMode setHg_error:error];
    
NSError* otherError = [HGAssertMode hg_error];
    
HGLog(@"%p \n%p", error, otherError);
  • 結(jié)果
驗(yàn)收成功
錯(cuò)誤,不一定是BUG

細(xì)心的同學(xué),又發(fā)現(xiàn)了一個(gè)BUG:在分類HGAssert中,我實(shí)現(xiàn)的是類方法,不是實(shí)例方法.其實(shí)都是類似的,不信你試試.這里要弄成類方法,是為了接下來的主題:<看我們的大標(biāo)題>

到這里,如果你都明白了上面的東西,說明你比我更優(yōu)秀!恭喜你,你趕快往下看吧!

進(jìn)入我們的大主題

我想要實(shí)現(xiàn)一個(gè)功能,計(jì)算一個(gè)數(shù)字的平方.代碼實(shí)現(xiàn)如下:

// 參數(shù)num職能是NSString與NSNumber是有效.
- (NSNumber*)square:(id)num {
    return [NSNumber numberWithInt:([num intValue]*[num intValue])];
}

咋一看,這個(gè)方法木有BUG.仔細(xì)一看,大大的有問題:如果我傳入的參數(shù)不是NSString與NSNumber中的一個(gè)呢?那就閃退了.當(dāng)然這里采取的解決方式很多,但是為了說明主題.接下來使用斷言來解決這個(gè)BUG.

  • 跟隨系統(tǒng)NSAssert的思想,也弄一個(gè)自己的宏斷言,代碼如下:
// 構(gòu)建錯(cuò)誤
#define HGBuildError(clazz, msg) \
NSError *error = [NSError errorWithDomain:msg code:205 userInfo:nil]; \
[clazz setHg_error:error];

/**
 * 斷言
 * @param condition   條件
 * @param returnValue 返回值
 */
#define HGAssertError(condition, returnValue, clazz, msg) \
[clazz setHg_error:nil]; \
if ((condition) == NO) { \
HGBuildError(clazz, msg); \
HGLog(@"%@",msg);\
return returnValue;\
}

到這里,自定義的宏斷言,就成功了.接下來,完善一下上面的方法.

  • 完善后的square:方法如下:
// 參數(shù)num職能是NSString與NSNumber是有效.
- (NSNumber*)square:(id)num {
    
    HGLog(@"斷言前");
    HGAssertError(([num isKindOfClass:[NSString class]] || [num isKindOfClass:[NSNumber class]]), nil, [NSNumber class], @"參數(shù)只能傳入NSString或者NSNumber的實(shí)例")
    HGLog(@"斷言后");
    
    return [NSNumber numberWithInt:([num intValue]*[num intValue])];
}
  • 檢驗(yàn)square:方法
    • 1
OK
  • 2
NO OK!

總結(jié)

  • 不用斷言,可以么?當(dāng)然可以,像上面的例子,不用斷言,一句判斷就能搞定.但是為什么我們還要學(xué)習(xí)斷言呢?這個(gè)問題是在是太經(jīng)典了!1+1=2,你知道么?那你為什么要知道1+1=2呢?
  • 經(jīng)檢驗(yàn),我們的斷言實(shí)現(xiàn)成功了!一個(gè)小小的功能,我寫了這么多的東西,這也在我的意料之外.其實(shí),我也是在<MJExtension>中見到的.看到"斷言"一詞,我是滿頭的霧霾!所以才自己實(shí)現(xiàn)了一遍.在過程中,難免有不當(dāng)之處,望評(píng)論指出!謝謝!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容