當(dāng)我們使用KVO監(jiān)聽(tīng)屬性的時(shí)候, 屬性必須要寫(xiě)對(duì),但是這個(gè)屬性沒(méi)有提示
[redView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
所以我們想要寫(xiě)一個(gè)自動(dòng)的提示宏來(lái)保證這個(gè)屬性可以寫(xiě)對(duì)
首先我們寫(xiě)這個(gè)宏之前先要學(xué)習(xí)一下 逗號(hào)表達(dá)式
->只取逗號(hào)最右邊的值
int a = (5,2,1);
NSLog(@"%d", a); // 打印的結(jié)果a=1
但是上面的寫(xiě)法會(huì)報(bào)警告Expression result unused
,意思是說(shuō)有個(gè)結(jié)果沒(méi)有用到
,但不影響結(jié)果
消除警告我們可以這樣寫(xiě) 前面添加void
,表示我不用你
int a = ((void)5,(void)2,1);
NSLog(@"%d", a);
所以這個(gè)宏我們可以這樣寫(xiě):
由于逗號(hào)表達(dá)式,所以這個(gè)宏,返回的只是逗號(hào)后面的內(nèi)容keyPath, 逗號(hào)前面的內(nèi)容可以隨便寫(xiě)
而逗號(hào)前面的我們?cè)趺磳?xiě)都不影響返回值, 但是為了可以使我們寫(xiě) frame時(shí)有自動(dòng)提示,所以逗號(hào)前面我們最好寫(xiě) objc.keyPath 這樣宏就有了自動(dòng)聯(lián)想功能
#define keyPath(objc, keyPath) ((void)objc.keyPath, keyPath)
這樣我們就可以這樣使用了keyPath(redView, frame);
這樣返回的就是 frame 文本, 但是我們需要的是 @"frame"的OC字符串
這里再介紹一點(diǎn): 宏里面的#號(hào)和##號(hào)的作用
在宏里面, ##的作用:連接2個(gè)標(biāo)識(shí)符
#define method(name) - (void)load##name {}
method(abc) //- (void)loadabc {}
method(ddd) //- (void)loadddd {}
method(ttt) //- (void)loadttt {}
在宏里面, #的作用:給右邊的標(biāo)識(shí)符加上雙引號(hào)""
#define test(name) @#name
test(abc) // @"abc"
所以我們按照下面這樣寫(xiě):
#define keyPath(objc, keyPath) ((void)objc.keyPath, #keyPath)
則打印: NSLog(@"%s",keyPath(redView, frame));
結(jié)果為: "frame"
然而我們需要的是@"frame"這樣的格式...
我們的操作還沒(méi)有完,所以我們只需要將C語(yǔ)言的字符串轉(zhuǎn)成OC字符串即可
// 我們只要將C語(yǔ)言的字符串用@()包裹,轉(zhuǎn)變成對(duì)象,即為OC字符串了
char *c = "abc";
NSLog(@"%@", @(c));
所以最終我們的宏寫(xiě)成這樣:
#define keyPath(objc, keyPath) @(((void)objc.keyPath, #keyPath))
所以開(kāi)頭我們的KVO監(jiān)聽(tīng)屬性就可以這樣寫(xiě)了
[redView addObserver:self forKeyPath:keyPath(redView, frame) options:NSKeyValueObservingOptionNew context:nil];