将来跳槽用iOS面试

iOS你在项目中用过 runtime 吗?举个例子

2018-02-10  本文已影响1649人  SunnyLeong

Objective-C 语言是一门动态语言,编译器不需要关心接受消息的对象是何种类型,接收消息的对象问题也要在运行时处理。

pragramming 层面的 runtime 主要体现在以下几个方面:

补充一个大家经常用的、但是很容易忽视的 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 的原理和用法可以看看官方文档

  1. Objective-C Runtime Programming Guide Interacting with the Runtime

  2. Objective-C Runtime Reference

  3. About Key-Value Coding

  4. Introduction to Key-Value Observing Programming Guide


20180212 更新


在引导页上使用了runtime

.h代码如下:

#import <UIKit/UIKit.h>

@interface UIViewController (KSGuid)<UICollectionViewDataSource,UICollectionViewDelegate,UIScrollViewDelegate>

//*实现引导页的控制器,外部不用调用即可实现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"

@interface KSGuidViewCell : UICollectionViewCell

@property (nonatomic, copy) NSString* imageName;
@property (nonatomic, strong) UIImageView* imageView;
@end
@implementation KSGuidViewCell

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _imageView = [[UIImageView alloc] initWithFrame:self.bounds];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
        _imageView.userInteractionEnabled = YES;
        [self.contentView addSubview:_imageView];
    }
    return self;
}
- (void)setImageName:(NSString *)imageName{
    if (_imageName != imageName) {
        _imageName = [imageName copy];
    }
    _imageView.image = [UIImage imageNamed:imageName];
}
@end

/************************以上是KSGuidViewCell,以下才是UIViewController+KSGuid******************************/

@implementation UIViewController (KSGuid)

#pragma mark- 
#pragma mark 这里是退出的按钮
- (UIButton*)removeBtn{
    //移除按钮样式
    UIButton* removeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [removeBtn addTarget:self action:@selector(removeGuidView) forControlEvents:UIControlEventTouchUpInside];
    removeBtn.hidden = (self.imageArray.count != 1);
    removeBtn.tag = RemoveBtn_tag;      //注意这里的tag
    
    //***********************这里面可以自定义*******************************//
    CGFloat btnW = 128;
    CGFloat btnH = 35;
    CGFloat btnX = CGRectGetMidX(self.view.frame) - btnW / 2;
    CGFloat btnY = CGRectGetMaxY(self.view.frame) * 0.83;
    removeBtn.frame = CGRectMake(btnX, btnY, btnW, btnH);
    
    removeBtn.layer.cornerRadius = 4;
    removeBtn.layer.borderColor = [UIColor whiteColor].CGColor;
    removeBtn.layer.borderWidth = 1.;
    
    [removeBtn setTitle:@"进入婚匠" forState:UIControlStateNormal];
    [removeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    removeBtn.titleLabel.font = [UIFont systemFontOfSize:18.];
     //********************自定义结束**********************************//
   
    return removeBtn;
}

#pragma mark-
#pragma mark 这里填充图片的名称
- (NSArray<NSString*>*)imageArray{
    return ImageArray;
}

+ (void)load{
    
    NSString* versoin = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    NSString* versionCache = [[NSUserDefaults standardUserDefaults] objectForKey:FIRST_IN_KEY];
    //启动时候首先判断是不是第一次
    
    if ([versoin isEqualToString:versionCache]) {
        return;
    }
    
    //以下代码只在程序安装初次运行时候执行
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        Method method1 = class_getInstanceMethod(self.class, @selector(viewDidLoad));
        Method method2 = class_getInstanceMethod(self.class, @selector(guidViewDidLoad));
        
        BOOL didAddMethod =
        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{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //这里的代码只在程序安装初次打开,并且在第一个控制器里面执行
        //初始化视图
        [self setupSubViews];
    });

    //这是调用工程里面的viewDidLoad
    [self guidViewDidLoad];
}

#pragma mark- 
#pragma mark 初始化视图

- (void)setupSubViews{
    
    //界面样式
    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.itemSize = [UIScreen mainScreen].bounds.size;
    flowLayout.minimumLineSpacing = 0;
    flowLayout.minimumInteritemSpacing = 0;
    flowLayout.sectionInset = UIEdgeInsetsZero;
    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

    UICollectionView* collectionView = [[UICollectionView alloc]
                      initWithFrame:self.view.bounds
                      collectionViewLayout:flowLayout];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    collectionView.pagingEnabled = YES;
    collectionView.showsVerticalScrollIndicator = NO;
    collectionView.showsHorizontalScrollIndicator = NO;
    collectionView.backgroundColor = [UIColor whiteColor];
    [collectionView registerClass:[KSGuidViewCell class] forCellWithReuseIdentifier:@"KSGuidViewCell"];
    
    collectionView.tag = CollectionView_Tag;
    [self.view addSubview:collectionView];
    
    [self.view addSubview:self.removeBtn];
    
    UIPageControl* control = [[UIPageControl alloc] init];
    
    CGFloat controlW = 170;
    CGFloat controlH = 20;
    CGFloat controlX = CGRectGetMidX(self.view.frame) - controlW / 2;
    CGFloat controlY = 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{
    return self.imageArray.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    KSGuidViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"KSGuidViewCell" forIndexPath:indexPath];
    cell.imageName = self.imageArray[indexPath.row];

    return cell;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
    NSUInteger index = 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 = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];

    [[NSUserDefaults standardUserDefaults] setObject:versoin forKey:FIRST_IN_KEY];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    [[self.view viewWithTag:Control_tag] removeFromSuperview];
    [[self.view viewWithTag:RemoveBtn_tag] removeFromSuperview];
    [[self.view viewWithTag:CollectionView_Tag] removeFromSuperview];
}


@end

demo详见github上
上一篇下一篇

猜你喜欢

热点阅读