要點
1.什么是鏈?zhǔn)秸Z法
2.Block聲明
3.實現(xiàn)&注意問題
4.場景&優(yōu)缺點
什么是鏈?zhǔn)秸Z法
OC中的RAC、Masonry姑曙、SnapKit等鏈?zhǔn)骄幊痰牡湫徒蠼唬蠹覒?yīng)該都熟悉了
Masonry
[testV mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.left.right.equalTo(self.view);
}];
點語法聲明式的調(diào)用,怎么實現(xiàn)呢伤靠?
----核心點語法和Block結(jié)合
OC并不像其它諸如Swift婿着、JS、Java等語言天然的語法支持醋界,所以我們只能利用OC的點語法來實現(xiàn)鏈?zhǔn)秸{(diào)用的語法糖,實現(xiàn)之前我們要首先對Block的聲明要熟悉
Block聲明
作為類的屬性
@property (nonatomic, copy) returnType (^blockName)(parameterTypes)
方法聲明返回值
- (Test *(^)(NSString *str))blk0;
作為方法參數(shù)
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
示例
- (void)methodTakesBlock:(void (^)(NSString *))blockName {
}
調(diào)用方法時傳入的參數(shù)
[self someMethodThatTakesABlock:^returnType (parameters) {...}];
示例:
-(IBAction)test2:(id)sender {
[self methodTakesBlock:^(NSString *name) {
NSLog(@"name:%@",name);
}];
}
寫在方法里作為局部變量
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
自定義Block類型
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
- returnType是返回值
- blockName是block名稱
- parameterTypes是參數(shù)
實現(xiàn)&注意問題
作者手懶就不拿項目中已有的或者從新寫個小demo了竟宋,這里以Masonry中為例
make.leading.trailing.bottom
對于這部分沒有入?yún)⒌逆準(zhǔn)綄崿F(xiàn)其實很簡單
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
get方法
- (MASConstraint *)leading {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading];
}
直接聲明一個屬性返回目標(biāo)對象,然后重寫get方法,我們重點看下equalTo(self.view)
- (MASConstraint * (^)(id attr))equalTo;
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
現(xiàn)在形纺,我們可以通過點語法調(diào)用getter方法的形式來調(diào)用方法丘侠,但是我們知道,getter方法是無法加參數(shù)的逐样,通過上面block的形式蜗字,調(diào)用get方法等同于執(zhí)行block打肝,調(diào)用block就可以隨意定義并傳入入?yún)ⅰ?br>
這里有些童鞋可能有點疑問make.top.bottom.left.right.equalTo(self.view);
equalTo的聲明是一個方法,能打點調(diào)用嗎挪捕?以及為什么可以粗梭?
我們平時調(diào)用屬性的setter/getter時,都是使用點語法調(diào)用级零,其本質(zhì)也是調(diào)用的方法断医,所以我們驗證下
.h
@interface TestObj : NSObject
- (NSString *)hello;
@end
.m
- (instancetype)init
{
self = [super init];
if (self) {
NSString *helloStr = self.hello;
}
return self;
}
我們通過以下轉(zhuǎn)寫成c/c++代碼
xcrun -sdk iphonesimulator clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mios-version-min=12.1 -fobjc-runtime=ios-12.1 -Wno-deprecated-declarations TestObj.m
后我們可以看到
static instancetype _I_TestObj_init(TestObj * self, SEL _cmd) {
self = ((TestObj *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("TestObj"))}, sel_registerName("init"));
if (self) {
NSString *helloStr = ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("hello"));
}
return self;
}
objc_msgSend最后找的SEL “hello”進行消息發(fā)送
或者在“NSString *helloStr = ”這一行下斷點Xcode菜單欄Debug->Debug Workflow->Always show。奏纪。查看匯編實現(xiàn)
那有的同學(xué)會有疑問聲明屬性和直接聲明方法有什么區(qū)別嗎鉴嗤?
答案還是有區(qū)別的。
主要還是在調(diào)用的時候系統(tǒng)給的友好提示方面序调。屬性聲明時在調(diào)用的時候會提示入?yún)㈩愋妥砉宦暶鞣椒ㄐ问皆谡{(diào)用的時候不會有任何提示,可能會稍有不便
修改為屬性后:
需要注意的地方
不管哪種方式发绢,我們都要注意內(nèi)存泄漏問題硬耍,這里主要注意是否循環(huán)引用問題,如果在get方法內(nèi)部返回的block沒有被當(dāng)前類強引用那么在block內(nèi)部可以直接引用self边酒,否則要類似weakSelf等避免循環(huán)引用.
場景&優(yōu)缺點
場景千千萬默垄,任何代碼實現(xiàn)部分理論上都可以鏈?zhǔn)絹韺崿F(xiàn)。重要的是我們要權(quán)衡到底有沒有必要
作者簡單列一些作者用到的場景
1.網(wǎng)絡(luò)請求:
[self requestWithHTTPMethod:@"POST"
baseURLString:nil
path:pathOrFullURLString
parameters:nil
files:nil
accountIdentityRequired:YES
usesCache:NO
from:nil
responseObjectClass:responseObjectClass
uploadProgressReporter:nil
succeededBlock:succeededBlock
failedBlock:failedBlock];
諸如此類會有很多入?yún)⑸醺伲皇侨雲(yún)⑦^多代碼不夠直觀也不規(guī)范口锭,二是很多入?yún)⒖赡芪幢匦枰獋鳌_@個時候就很需要鏈?zhǔn)秸Z法介杆,按需配置入?yún)⑼瑫r便于閱讀
改版后:
SPTNetworkEngine.spt_dataxEngine.get(@"requestPath")
.handleJSONResponse(^(id _Nonnull JSONObject) {
}).handleFailure(^(NSError * _Nonnull error) {
}).start();
需要請求入?yún)⒌臅r候加上即可
.usesAccountIdentity(SPTNetworkAccountIdentityUsageNone)
.withParameters(parameters)
2.項目中特殊業(yè)務(wù)彈窗實現(xiàn)
whiteListView.withViews(targetView, window)
.withContents(^(SPTChatRoomWhiteListViewConfig * _Nonnull viewConfig) {
//viewConfig;//whiteListView配置項交由user配置擴展,不傳則內(nèi)部默認(rèn)配置
}).selectedAction(^(id<SPTChatRoomWhiteListModelProtocol> _Nonnull selectedItem) {
}).cancelAction(^{
}).show(YES);
以上甚至可以直接調(diào)用.show(YES);
3.甚至比Masonry更深入的autolayout鏈?zhǔn)椒庋b
self.likePKView.sn_centerXToSuperView()
.sn_topToSuperView().sn_equal(-spt_screen_adaptive_float(52))
.sn_height(@(spt_screen_adaptive_float(52)))
.sn_leftToSuperView()
.sn_rightToSuperView()
.sn_layout();
- ...
以上只是部分作者項目中實踐的場景鹃操。更多的需要我們自己去權(quán)衡,畢竟鏈?zhǔn)秸Z法封裝的代碼總是比基本語法耗時春哨,既要提高開發(fā)效率, 同時也要保證APP運行速度, 所以要量力而行, 不能太泛濫!