runtime!runtime! runtime! 几乎每个iOS开发从业者在面试时都会被问到什么是runtime?!有什么用?!介于本人的一点从业经验和过往学习,下面就runtime做一个简单粗暴的介绍。
Objective-C 语言是一门动态语言,编译器不需要关心接受消息的对象是何种类型,接收消息的对象问题也要在运行时处理。
pragramming 层面的 runtime 主要体现在以下几个方面:
消息发送Messaging
方法调配Method Swizzling
“类对象” NSProxyFoundation | Apple Developer Documentation
KVC、KVOAbout Key-Value Coding
补充一个大家经常用的、但是很容易忽视的 runtime 应用吧:
动态获取 class 和 slector
NSClassFromString(@"MyClass");NSSelectorFromString(@"showShareActionSheet");
给分类添加属性和KVC/KVO了。
大多数情况下,我们是不会直接用 runtime 写业务代码的,所以,大部分时候,我们只会在一些平时用起来很方便的框架里面,看到有用到 runtime 的黑魔法(当然你也可以自己造轮子)。runtime 用起来的最大感受就是,底层写一堆 runtime,外面用起来超清爽!对于那些“代码艺术家”来说,简直是爽到爆。
我在项目中使用runtime比较少:
1.在category中添加属性
2.修改系统方法,如重写objectAtIndex方法阻止数组越界崩溃。
大概说说一下自己在开发中用到的一些基于 runtime 的开源框架吧,具体应用自己可以去看看源码:
1.Aspects(AOP必备,“取缔” baseVC,无侵入埋点)
2.MJExtension(JSON 转 model,一行代码实现 NSCoding 协议的自动归档和解档)
3.JSPatch(动态下发 JS 进行热修复)
4.NullSafe(防止因发 unrecognised messages 给 NSNull 导致的崩溃)
5.UITableView-FDTemplateLayoutCell(自动计算并缓存 table view 的 cell 高度)
6.UINavigationController+FDFullscreenPopGesture(全屏滑动返回)
runtime 的原理和用法可以看看官方文档:
Objective-C Runtime Programming GuideInteracting with the Runtime
Introduction to Key-Value Observing Programming Guide
20180212 更新
在引导页上使用了runtime
.h代码如下:
#import<UIKit/UIKit.h>@interfaceUIViewController(KSGuid)//*实现引导页的控制器,外部不用调用即可实现GuidView,可以修改下面的图片*/@end/*这里是要展示的图片,修改即可,当然不止三个 1242 * 2208的分辨率最佳,如果在小屏手机上显示不全,最好要求UI重新设计图片*/#define ImageArray @[@"guid01",@"guid02",@"guid03",@"guid04"]/** pageIndicatorTintColor*/#define pageTintColor [[UIColor whiteColor] colorWithAlphaComponent:0.5];/** currentPageIndicatorTintColor*/#define currentTintColor [UIColor whiteColor];/*
如果要修改立即体验按钮的样式
重新- (UIButton*)removeBtn方法即可
*/
.m代码如下
#import"UIViewController+KSGuid.h"#import<objc/runtime.h>#define CollectionView_Tag 15#define RemoveBtn_tag 16#define Control_tag 17#define FIRST_IN_KEY @"FIRST_IN_KEY"@interfaceKSGuidViewCell:UICollectionViewCell@property(nonatomic,copy)NSString* imageName;@property(nonatomic,strong)UIImageView* imageView;@end@implementationKSGuidViewCell- (instancetype)initWithFrame:(CGRect)frame{self= [superinitWithFrame:frame];if(self) { _imageView = [[UIImageViewalloc] initWithFrame:self.bounds]; _imageView.contentMode =UIViewContentModeScaleAspectFill; _imageView.userInteractionEnabled =YES; [self.contentView addSubview:_imageView]; }returnself;}- (void)setImageName:(NSString*)imageName{if(_imageName != imageName) { _imageName = [imageNamecopy]; } _imageView.image = [UIImageimageNamed:imageName];}@end/************************以上是KSGuidViewCell,以下才是UIViewController+KSGuid******************************/@implementationUIViewController(KSGuid)#pragma mark- #pragma mark 这里是退出的按钮- (UIButton*)removeBtn{//移除按钮样式UIButton* removeBtn = [UIButtonbuttonWithType:UIButtonTypeCustom]; [removeBtn addTarget:selfaction:@selector(removeGuidView) forControlEvents:UIControlEventTouchUpInside]; removeBtn.hidden = (self.imageArray.count !=1); removeBtn.tag = RemoveBtn_tag;//注意这里的tag//***********************这里面可以自定义*******************************//CGFloatbtnW =128;CGFloatbtnH =35;CGFloatbtnX =CGRectGetMidX(self.view.frame) - btnW /2;CGFloatbtnY =CGRectGetMaxY(self.view.frame) *0.83; removeBtn.frame =CGRectMake(btnX, btnY, btnW, btnH); removeBtn.layer.cornerRadius =4; removeBtn.layer.borderColor = [UIColorwhiteColor].CGColor; removeBtn.layer.borderWidth =1.; [removeBtn setTitle:@"进入婚匠"forState:UIControlStateNormal]; [removeBtn setTitleColor:[UIColorwhiteColor] forState:UIControlStateNormal]; removeBtn.titleLabel.font = [UIFontsystemFontOfSize:18.];//********************自定义结束**********************************//returnremoveBtn;}#pragma mark-#pragma mark 这里填充图片的名称- (NSArray*)imageArray{returnImageArray;}+ (void)load{NSString* versoin = [[[NSBundlemainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];NSString* versionCache = [[NSUserDefaultsstandardUserDefaults] objectForKey:FIRST_IN_KEY];//启动时候首先判断是不是第一次if([versoin isEqualToString:versionCache]) {return; }//以下代码只在程序安装初次运行时候执行staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{ Method method1 = class_getInstanceMethod(self.class,@selector(viewDidLoad)); Method method2 = class_getInstanceMethod(self.class,@selector(guidViewDidLoad));BOOLdidAddMethod = class_addMethod(self.class,@selector(viewDidLoad), method_getImplementation(method2), method_getTypeEncoding(method2));if(didAddMethod) { class_replaceMethod(self.class,@selector(guidViewDidLoad), method_getImplementation(method1), method_getTypeEncoding(method1)); }else{ method_exchangeImplementations(method1, method2); } });}- (void)guidViewDidLoad{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{//这里的代码只在程序安装初次打开,并且在第一个控制器里面执行//初始化视图[selfsetupSubViews]; });//这是调用工程里面的viewDidLoad[selfguidViewDidLoad];}#pragma mark- #pragma mark 初始化视图- (void)setupSubViews{//界面样式UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayoutalloc] init]; flowLayout.itemSize = [UIScreenmainScreen].bounds.size; flowLayout.minimumLineSpacing =0; flowLayout.minimumInteritemSpacing =0; flowLayout.sectionInset =UIEdgeInsetsZero; flowLayout.scrollDirection =UICollectionViewScrollDirectionHorizontal;UICollectionView* collectionView = [[UICollectionViewalloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout]; collectionView.dataSource =self; collectionView.delegate =self; collectionView.pagingEnabled =YES; collectionView.showsVerticalScrollIndicator =NO; collectionView.showsHorizontalScrollIndicator =NO; collectionView.backgroundColor = [UIColorwhiteColor]; [collectionView registerClass:[KSGuidViewCellclass] forCellWithReuseIdentifier:@"KSGuidViewCell"]; collectionView.tag = CollectionView_Tag; [self.view addSubview:collectionView]; [self.view addSubview:self.removeBtn];UIPageControl* control = [[UIPageControlalloc] init];CGFloatcontrolW =170;CGFloatcontrolH =20;CGFloatcontrolX =CGRectGetMidX(self.view.frame) - controlW /2;CGFloatcontrolY =CGRectGetMaxY(self.view.frame) -38; control.frame =CGRectMake(controlX, controlY, controlW, controlH); control.numberOfPages = ImageArray.count; control.pageIndicatorTintColor = pageTintColor; control.currentPageIndicatorTintColor = currentTintColor; control.tag = Control_tag; [self.view addSubview:control];}#pragma mark-#pragma mark UICollectionViewDataSource- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section{returnself.imageArray.count;}- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath{ KSGuidViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"KSGuidViewCell"forIndexPath:indexPath]; cell.imageName =self.imageArray[indexPath.row];returncell;}- (void)scrollViewDidScroll:(UIScrollView*)scrollView{NSUIntegerindex = scrollView.contentOffset.x /CGRectGetWidth(self.view.frame); [self.view viewWithTag:RemoveBtn_tag].hidden = (index !=self.imageArray.count -1);UIPageControl* control =[self.view viewWithTag:Control_tag]; control.currentPage = index; }- (void)removeGuidView{NSString* versoin = [[[NSBundlemainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; [[NSUserDefaultsstandardUserDefaults] setObject:versoin forKey:FIRST_IN_KEY]; [[NSUserDefaultsstandardUserDefaults] synchronize]; [[self.view viewWithTag:Control_tag] removeFromSuperview]; [[self.view viewWithTag:RemoveBtn_tag] removeFromSuperview]; [[self.view viewWithTag:CollectionView_Tag] removeFromSuperview];}@end