iOS鏈?zhǔn)秸Z法深入實踐

要點

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)


222.png

那有的同學(xué)會有疑問聲明屬性和直接聲明方法有什么區(qū)別嗎?
答案還是有區(qū)別的。
主要還是在調(diào)用的時候系統(tǒng)給的友好提示方面。屬性聲明時在調(diào)用的時候會提示入?yún)㈩愋?;聲明方法形式在調(diào)用的時候不會有任何提示,可能會稍有不便


333.png

修改為屬性后:
444.png

需要注意的地方
不管哪種方式,我們都要注意內(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();
  1. ...

以上只是部分作者項目中實踐的場景。更多的需要我們自己去權(quán)衡,畢竟鏈?zhǔn)秸Z法封裝的代碼總是比基本語法耗時,既要提高開發(fā)效率, 同時也要保證APP運行速度, 所以要量力而行, 不能太泛濫!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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