iOS你在項(xiàng)目中用過(guò) runtime 嗎牙寞?舉個(gè)例子

Objective-C 語(yǔ)言是一門(mén)動(dòng)態(tài)語(yǔ)言,編譯器不需要關(guān)心接受消息的對(duì)象是何種類(lèi)型,接收消息的對(duì)象問(wèn)題也要在運(yùn)行時(shí)處理间雀。

pragramming 層面的 runtime 主要體現(xiàn)在以下幾個(gè)方面:

補(bǔ)充一個(gè)大家經(jīng)常用的、但是很容易忽視的 runtime 應(yīng)用吧:

動(dòng)態(tài)獲取 class 和 slector

NSClassFromString(@"MyClass");
NSSelectorFromString(@"showShareActionSheet");

給分類(lèi)添加屬性KVC/KVO了惹挟。

大多數(shù)情況下茄螃,我們是不會(huì)直接用 runtime 寫(xiě)業(yè)務(wù)代碼的,所以连锯,大部分時(shí)候责蝠,我們只會(huì)在一些平時(shí)用起來(lái)很方便的框架里面,看到有用到 runtime 的黑魔法(當(dāng)然你也可以自己造輪子)萎庭。runtime 用起來(lái)的最大感受就是霜医,底層寫(xiě)一堆 runtime,外面用起來(lái)超清爽驳规!對(duì)于那些“代碼藝術(shù)家”來(lái)說(shuō)肴敛,簡(jiǎn)直是爽到爆。

我在項(xiàng)目中使用runtime比較少:

1.在category中添加屬性
2.修改系統(tǒng)方法吗购,如重寫(xiě)objectAtIndex方法阻止數(shù)組越界崩潰医男。

大概說(shuō)說(shuō)一下自己在開(kāi)發(fā)中用到的一些基于 runtime 的開(kāi)源框架吧,具體應(yīng)用自己可以去看看源碼:

1.Aspects(AOP必備捻勉,“取締” baseVC镀梭,無(wú)侵入埋點(diǎn))
2.MJExtension(JSON 轉(zhuǎn) model,一行代碼實(shí)現(xiàn) NSCoding 協(xié)議的自動(dòng)歸檔和解檔)
3.JSPatch(動(dòng)態(tài)下發(fā) JS 進(jìn)行熱修復(fù))
4.NullSafe(防止因發(fā) unrecognised messages 給 NSNull 導(dǎo)致的崩潰)
5.UITableView-FDTemplateLayoutCell(自動(dòng)計(jì)算并緩存 table view 的 cell 高度)
6.UINavigationController+FDFullscreenPopGesture(全屏滑動(dòng)返回)


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 更新


在引導(dǎo)頁(yè)上使用了runtime

.h代碼如下:

#import <UIKit/UIKit.h>

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

//*實(shí)現(xiàn)引導(dǎo)頁(yè)的控制器踱启,外部不用調(diào)用即可實(shí)現(xiàn)GuidView,可以修改下面的圖片*/

@end

/*這里是要展示的圖片报账,修改即可,當(dāng)然不止三個(gè)  1242 * 2208的分辨率最佳,如果在小屏手機(jī)上顯示不全,最好要求UI重新設(shè)計(jì)圖片*/

#define ImageArray @[@"guid01",@"guid02",@"guid03",@"guid04"]

/** pageIndicatorTintColor*/
#define pageTintColor [[UIColor whiteColor] colorWithAlphaComponent:0.5];
/** currentPageIndicatorTintColor*/
#define currentTintColor [UIColor whiteColor];


/*
如果要修改立即體驗(yàn)按鈕的樣式
重新- (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:@"進(jìn)入婚匠" forState:UIControlStateNormal];
    [removeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    removeBtn.titleLabel.font = [UIFont systemFontOfSize:18.];
     //********************自定義結(jié)束**********************************//
   
    return removeBtn;
}

#pragma mark-
#pragma mark 這里填充圖片的名稱(chēng)
- (NSArray<NSString*>*)imageArray{
    return ImageArray;
}

+ (void)load{
    
    NSString* versoin = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    NSString* versionCache = [[NSUserDefaults standardUserDefaults] objectForKey:FIRST_IN_KEY];
    //啟動(dòng)時(shí)候首先判斷是不是第一次
    
    if ([versoin isEqualToString:versionCache]) {
        return;
    }
    
    //以下代碼只在程序安裝初次運(yùn)行時(shí)候執(zhí)行
    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, ^{
        //這里的代碼只在程序安裝初次打開(kāi)埠偿,并且在第一個(gè)控制器里面執(zhí)行
        //初始化視圖
        [self setupSubViews];
    });

    //這是調(diào)用工程里面的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詳見(jiàn)github上
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末透罢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子冠蒋,更是在濱河造成了極大的恐慌羽圃,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抖剿,死亡現(xiàn)場(chǎng)離奇詭異朽寞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)斩郎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén)脑融,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人孽拷,你說(shuō)我怎么就攤上這事吨掌“氡В” “怎么了脓恕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵膜宋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我炼幔,道長(zhǎng)秋茫,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任乃秀,我火速辦了婚禮肛著,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘跺讯。我一直安慰自己枢贿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布刀脏。 她就那樣靜靜地躺著局荚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪愈污。 梳的紋絲不亂的頭發(fā)上耀态,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音暂雹,去河邊找鬼首装。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杭跪,可吹牛的內(nèi)容都是我干的仙逻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涧尿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼桨醋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起现斋,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤苗桂,失蹤者是張志新(化名)和其女友劉穎韵卤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狈定,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巷挥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烹卒。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖倦西,靈堂內(nèi)的尸體忽然破棺而出能真,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布粉铐,位于F島的核電站疼约,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蝙泼。R本人自食惡果不足惜程剥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汤踏。 院中可真熱鬧织鲸,春花似錦、人聲如沸溪胶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)哗脖。三九已至盾饮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間懒熙,已是汗流浹背丘损。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留工扎,地道東北人徘钥。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肢娘,于是被迫代替她去往敵國(guó)和親呈础。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1.設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式拘荡,并簡(jiǎn)要敘述臼节?設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類(lèi)型...
    龍飝閱讀 2,159評(píng)論 0 12
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,105評(píng)論 1 32
  • 設(shè)計(jì)模式是什么珊皿? 你知道哪些設(shè)計(jì)模式网缝,并簡(jiǎn)要敘述? 設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn)蟋定,就是用比較成熟的邏輯去處理某一種類(lèi)型的...
    卑微的戲子閱讀 623評(píng)論 0 1
  • 1.設(shè)計(jì)模式是什么粉臊? 你知道哪些設(shè)計(jì)模式,并簡(jiǎn)要敘述驶兜? 設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn)扼仲,就是用比較成熟的邏輯去處理某一種類(lèi)...
    司馬DE晴空閱讀 1,295評(píng)論 0 7
  • 今日天熱远寸,沒(méi)什么風(fēng)。 早上上完1/2節(jié)就和班里女生忙著買(mǎi)男生節(jié)禮物屠凶。他們說(shuō)這是廣金不成文習(xí)俗驰后,男生要給女生過(guò)女生節(jié)...
    Rinky_e80c閱讀 181評(píng)論 0 0