MangoFix:iOS热修复另辟蹊径
今天向大家介紹的是iOS熱修復(fù)的另一解決方案:MangoFix。介紹他的原因是他和傳統(tǒng)的iOS熱修復(fù)使用JavaScript bridge 的方式完全不同,MangoFix是一個語法和OC語法非常類似的DSL,其語言本身的設(shè)計目標(biāo)就是為了解決iOS熱修復(fù)問題,所以在使用的便捷程度和性能方面都要遠(yuǎn)遠(yuǎn)超過傳統(tǒng)的iOS 熱修復(fù)SDK,比如JSPatch。下面從以下幾點介紹MangoFix,更具體的請參考GitHub文檔和MangoFix單元測試。
1、如何加載一個MangoFix腳本
-
1 首先通過CocoaPods安裝MangoFix :pod 'MangoFix'
-
2 引入MangoFix頭文件:#import <MangoFix/MangoFix.h>
-
3 創(chuàng)建MangoFix腳本執(zhí)行上下文對象MFContext實例
-
4 運行MangoFix腳本文件
示例代碼如下:
NSString *path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"mg"];NSURL *scriptUrl = [NSURL fileURLWithPath:path];MFContext *context = [[MFContext alloc] init];[context evalMangoScriptWithURL:scriptUrl]; 復(fù)制代碼2、MangoFix如何修復(fù)OC對象(類)方法
MangoFix可以替換或創(chuàng)建任意OC對象實例方法或類方法,語法和OC類似,不過在類的定義上采用class關(guān)鍵字。下面示例:
class MFInstanceMethodReplaceTest : NSObject {- (BOOL)testInstanceMethodReplace{return YES; }} 復(fù)制代碼對于類方法的替換只需將方法返回值類型前的-修改為+即可。
需要注意的是:
1、繼承的父類不可以省略。
3、MangoFix如何為對象添加屬性
MangoFix中為對象添加屬性和OC一樣,支持的修飾符有: weak、 strong、 copy、 assign、 nonatomic、atomic。下面看一下示例代碼:
class MFObjectPropertyTest : NSObject{@property(nonatomic, copy)NSString *propertyName;- (NSString *)testObjectPropertyTest{return self.strTypeProperty; }} 復(fù)制代碼需要注意的是:
1、屬性不支持class修飾符。
2、MangoFix是通過objc_setAssociatedObject實現(xiàn)屬性值的存儲,但是MangoFix在解析時候做了處理,訪問屬性值也可以通過_propertyName這種方式進(jìn)行訪問。
4、MangoFix中如何使用block
在MangoFix對OC中block類型聲明過于復(fù)雜做了簡化,用Block關(guān)鍵字表示block類型,block的定義則和OC相同,示例代碼如下:
class MFMethodParameterListAndReturnValueTest : NSObject{- (Block)testMethodParameterListAndReturnValueWithString:(NSString *)str block:(Block)block{NSMutableDictionary *dic = @{}.mutableCopy();dic[@"param1"] = str + @"MangoFix";dic[@"param2"] = block(@"MangoFix");Block retBlock = ^NSDictionary *(/*不能加void*/){return dic;};return retBlock; }} 復(fù)制代碼需要注意的是:
1、在無參block定義時,不可以加void聲明。
2、Block關(guān)鍵字后面不需要加*運算符。
5、如何解決Block循環(huán)引用問題
MangoFix在1.1.7版本中添加__weak和__strong變量修飾符,可以像OC原生一樣解決Block循環(huán)引用問題,使用示例如下:
@interface MyController : UIViewController@property(nonatomic,copy) id block;@end 復(fù)制代碼class MyController: UIViewController { - (void)viewDidLoad {super.viewDidLoad();__weak id weakSelf = self;self.block = ^{__strong id strongSelf = weakSelf;NSLog(weakSelf);}; }} 復(fù)制代碼上部分是OC代碼,下部分是MangoFix代碼,需要注意的是,__weak和__strong只能放在變量類型之前。
6、MangoFix中如何使用GCD
MangoFix中已經(jīng)內(nèi)置的GCD API,使用方法和 OC相同,對于需要擴(kuò)展的C函數(shù),可以參考下面如何在MangoFix中注入全局對象的描述,GCD使用示例如下:
class MFGCDTest : NSObject {- (void)testGCDWithCompletionBlock:(Block)completion{dispatch_queue_t queue = dispatch_queue_create("com.plliang19.mango", DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{completion(@"success");}); }- (void)testGCDAfterWithCompletionBlock:(Block)completion{dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{completion(@"success");}); }}class MFDispatchSourceTest : NSObject{- (NSInteger)testDispatchSource{NSInteger count = 0;dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);dispatch_source_set_event_handler(timer, ^{count++;if (count == 10) {dispatch_suspend(timer);dispatch_semaphore_signal(semaphore);}});dispatch_resume(timer);dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);return count; }}復(fù)制代碼7、static變量支持
MangoFix1.2.0版本中增加了對static變量進(jìn)行了支持,MangoFix 的static變量和C語言中static變量特性基本一致。MangoFix中通過一張全局表對static變量進(jìn)行管理,static變量只會初始化一致,static變量生命周期為從第一次初始化到App退出,static變量作用域和自動變量作用域一致,所以可以在不同作用域范圍內(nèi),創(chuàng)建變量名相同的static變量也是不會沖突的。
8、取地址運算符
MangoFix1.2.0版本中,增加了對取地址運算符&的支持,利用取地址運算符和static變量,MangoFix便能對GCD中的dispatch_once函數(shù)做很好的支持,比如下面的MangoFix示例代碼:
class MFGetAddressOperatorTest : NSObject{- (NSInteger)testGetAddressOperator{static int i = 0;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{i++;});return i; }} 復(fù)制代碼9、如何在MangoFix中注入全局對象
MangoFix中MFContext對象提供了 - (void)setObject:(MFValue *)value forKeyedSubscript:(NSObject <NSCopying> *)key;方法,便于用戶向執(zhí)行上下文中注入全局對象,比如在OC代碼中執(zhí)行下面代碼:
context[@"globalVar"] = [MFValue valueInstanceWithBOOL:YES]; context[@"MyLog"] = [MFValue valueInstanceWithBlock:^void (id obj){NSLog(@"%@",obj);}]; 復(fù)制代碼分別表示向context注入全局的BOOL變量globalVar和名為MyLog的block。
10、MangoFix中如何針對不同App版本做不同的熱修復(fù)處理
MangoFix提供了條件注解#If(conditionExpr),可以在運行時做判斷注解所作用的類、屬性、方法是否使能,先看一下示例代碼:
class MFConditionalReplaceTest : NSObject{#If($systemVersion.doubleValue() >= 10.0 ) - (BOOL)testConditionalReplace{return NO; }} 復(fù)制代碼上面代碼表示只有當(dāng)$systemVersion.doubleValue()值大于10.0才會對 - (BOOL)testConditionalReplace方法進(jìn)行替換。 MangoFxi中已經(jīng)內(nèi)置了$systemVersion 、$appVersion、$buildVersion等和版本相關(guān)的全局變量,分別表示:[UIDevice currentDevice].systemVersion、CFBundleShortVersionString、CFBundleVersion,當(dāng)然如果用戶覺得不夠還可以自己向MangoFix執(zhí)行上下文中注入自定義的全局變量。
11、C函數(shù)變量
早期MangoFix版本中已經(jīng)將一些常用的C函數(shù)進(jìn)行預(yù)埋,用戶也可以自定義進(jìn)行預(yù)埋,但是總有一些需要調(diào)用的C函數(shù)沒有預(yù)埋到,所以MangoFix 1.3.0版本開始支持C函數(shù)變量,可以做到C函數(shù)聲明即用無需預(yù)埋,C函數(shù)變量的定義和其他語言中的泛型很類似,格式如: CFunction<returnType,arg1Type,arg2Type,...> func, 尖括號中第一個type是函數(shù)返回值類型,其他的為函數(shù)形參類型,現(xiàn)在支持的類型有:void、BOOL、int、long、int8_t、int16_t、int32_t、int64_t、u_int、u_long、u_int8_t```、u_int16_t、u_int32_t、u_int64_t、size_t、float、double、CGFloat、char *、void *、id、SEL、Class、struct structName,對于其他數(shù)據(jù)類型,要根據(jù)數(shù)據(jù)類型的大小選擇上面一種數(shù)據(jù)類型,而C函數(shù)變量的值用CFunction("function_name")獲取,對于dlsym和dlopen這兩個函數(shù)已被禁止動態(tài)調(diào)用,另外要注意的是CFunction("function_name")``只支持獲取動態(tài)鏈接的C函數(shù)。 下面我們看一段示例代碼:
int NSDocumentDirectory = 9; int NSUserDomainMask = 1;int O_WRONLY = 0x0001; uint S_IRWXU = 0000700;CFunction<id, int, int, BOOL> NSSearchPathForDirectoriesInDomains = CFunction("NSSearchPathForDirectoriesInDomains"); CFunction<int, char *, int, int> open = CFunction("open"); CFunction<size_t, int, void *, size_t> write = CFunction("write"); CFunction<int, int> close = CFunction("close");class MFFuncDeclareTest : NSObject{- (void)testFuncDeclare{NSString *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;NSString *path = doc.stringByAppendingPathComponent:(@"MangoFxi.html");NSFileManager *fileManager = NSFileManager.defaultManager();if (!fileManager.fileExistsAtPath:(path)) {BOOL ret = fileManager.createFileAtPath:contents:attributes:(path, nil, nil);if (!ret) {NSLog(@"創(chuàng)建文件失敗");return;}}NSLog(path);int fd = open(path.UTF8String,O_WRONLY, S_IRWXU);if (fd < 0) {NSLog(@"打開文件失敗");return;}NSURL *url = NSURL.URLWithString:(@"https://github.com/YPLiang19/Mango");NSData *data = NSData.dataWithContentsOfURL:(url);write(fd, data.bytes, data.length);close(fd); }}復(fù)制代碼12、類型別名
MangoFix 1.3.0版本開始支持 typedef功能,格式為:typedef existingType newType; 比如:
typedef long alias_long; alias_long var = 10; 復(fù)制代碼13、MangoFix中自定義結(jié)構(gòu)體的使用要注意什么
MangoFix腳本中使用結(jié)構(gòu)體,原則上是要先對結(jié)構(gòu)體使用declare struct進(jìn)行聲明,但是MangoFix已經(jīng)對常用的結(jié)構(gòu)已經(jīng)內(nèi)置聲明,已內(nèi)置聲明的結(jié)構(gòu)如下: CGPoint、CGSize、CGRect、CGAffineTransform、CGVector、NSRange、UIOffset、UIEdgeInsets、CATransform3D
在MangoFix中使用未聲明的結(jié)構(gòu)體,需要做如下聲明:
declare struct MFCustomStruct {typeEncoding:"{MFCustomStruct=dd}",//@encode(struct MFCustomStruct)keys:x,y } 復(fù)制代碼特別需要注意的是:
1、在定義一個結(jié)構(gòu)體變量時,需要在前面加入struct關(guān)鍵字:
14、Masonry鏈?zhǔn)骄幊谭绞皆贛angoFix中如何編寫
有同學(xué)疑問對Masonry中的鏈?zhǔn)骄幊淘贛angoFix如何編寫呢?其實這個寫起來也是大同小異。需要注意的是,在MangoFix中對調(diào)用的方法如果是無參的,那么可以省去調(diào)用后面的一對括號,但是如果方法返回的是一個block對象,那么這對括號就不能省略,應(yīng)為此時如果省略了方法調(diào)用括號,那么MangoFix解析器就無法知道,此時用戶是想調(diào)用OC的對象方法,還是調(diào)用方法返回的block。下面是一個OC和MangoFix分別調(diào)用Masonry官方示例代碼的對比:
UIView *superview = self.view;UIView *view1 = [[UIView alloc] init];view1.translatesAutoresizingMaskIntoConstraints = NO;view1.backgroundColor = [UIColor greenColor];[superview addSubview:view1];UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);[view1 mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic fillermake.left.equalTo(superview.mas_left).with.offset(padding.left);make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);make.right.equalTo(superview.mas_right).with.offset(-padding.right);}]; 復(fù)制代碼 UIView *superview = self.view;UIView *view1 = UIView.alloc().init();view1.translatesAutoresizingMaskIntoConstraints = NO;view1.backgroundColor = UIColor.greenColor();superview.addSubview:(view1);struct UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);view1.mas_makeConstraints:(^(MASConstraintMaker *make) {make.top.equalTo()(superview.mas_top).with.offset()(padding.top); //with is an optional semantic fillermake.left.equalTo()(superview.mas_left).with.offset()(padding.left);make.bottom.equalTo()(superview.mas_bottom).with.offset()(-padding.bottom);make.right.equalTo()(superview.mas_right).with.offset()(-padding.right);}); 復(fù)制代碼上面部分是OC代碼,下面部分是MangoFix代碼,主要區(qū)別就是MangoFix代碼在equalTo和offset后面多了一對括號,就是避免MangoFix解析器產(chǎn)生歧義。再者就是MangoFix中UIEdgeInsets前的struct關(guān)鍵字不能省略。
15、MangoFix性能的如何
根據(jù)本人測試,MangoFix的初始化速度是JSPatch的10倍左右,運行速度是JSPatch的2~5倍,內(nèi)存占用方面并無太大區(qū)別。
16、MangoFix還有哪些不足
- MangoFix不支持可變參數(shù)方法的調(diào)用和替換。
- MangoFix調(diào)用C函數(shù),需要預(yù)先通過注入全局對象方式,通過block將C函數(shù)預(yù)先埋入(1.3.0版本已支持C函數(shù)變量聲明即用無需預(yù)埋)。
- MangoFix不支持替換C函數(shù)。
轉(zhuǎn)載于作者:知水為命
鏈接:www.jianshu.com/p/7ae91a2da…
給大家推薦一個優(yōu)秀的iOS交流群,群里的伙伴們都是非常優(yōu)秀的iOS開發(fā)人員,我們專注于技術(shù)的分享與技巧的交流,大家可以在群討論技術(shù),交流學(xué)習(xí)。歡迎大家的加入761407670(密碼123)。
轉(zhuǎn)載于:https://juejin.im/post/5d20448cf265da1bbc6ff6bc
總結(jié)
以上是生活随笔為你收集整理的MangoFix:iOS热修复另辟蹊径的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 捷信分期上征信吗?逾期多久会上征信?
- 下一篇: Django打造大型企业官网-项目部署