2018-12-22
iOS面试题总结
https://www.jianshu.com/p/1798ba01e9ef
平铺模式,一般由scrollView和pageControl组合而成的展示方式。手机自带的天气比较典型。
标签模式,tabBar的展示方式,这个比较常见。
树状模式,tableView的多态展示方式,常见的9宫格、系统自带的邮箱等展现方式。
属性列表文件-- NSUserDefaults的存储,实际是本地生成一个plist文件,将所需属性存储在plist文件中
对象归档 -- 本地创建文件并写入数据,文件类型不限
SQLite 数据库 -- 本地创建数据库文件,进行数据处理
CoreData -- 同数据库处理思想相同,但实现方式不同
iOS单元测试框架有哪些
OCUnit是OC官方测试框架,现在被XCTest所取代。
XCTest是与Foundation框架平行的测试框架。
GHUnit是第三方的测试框架。github地址
OCMock都是第三方的测试框架。github地址
ios7 层协议,tcp四层协议及如何对应的?
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>
@interface UIViewController(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"
@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*)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