1.object-c类的类型和数据变量的类型都是在运行是肯定的,而不是在编译时肯定。例如:多态特性,咱们可使用父类对象来指向子类对象,而且能够用来调用子类的方法。运行时(runtime)特性,咱们能够动态的添加方法,或者替换方法。html
MVC:简单来讲就是,逻辑、试图、数据进行分层,实现解耦。
MVVM:是Model-View-ViewMode模式的简称。由视图(View)、视图模型(ViewModel)、模型(Model)三部分组成.比MVC更加释放控制器臃肿,将一部分逻辑(耗时,公共方法,网络请求等)和数据的处理等操做从控制器里面搬运到ViewModel
中
MVVM的特色:git
- 低耦合。View能够独立于Model变化和修改,一个ViewModel能够绑定到不一样的View上,当View变化的时候Model能够不变,当Model变化的时候View也能够不变。
- 独立开发。开发人员能够专一与业务逻辑和数据的开发(ViewModel)。设计人员能够专一于界面(View)的设计。
MVP:本小编没有接触,但愿能够获得你们的帮助。能够在下面留言。github
代理是使用weak来修饰的。1.使用weak是为了不循环引用。2.当使用weak修饰的属性,当对象释放的时候,系统会对属性赋值
nil
,object-c有个特性就是对nil
对象发送消息也就是调用方法,不会cash。
delegate:表示代理,代理可让A对象通知B对象,我(A)发生的变化,前提B遵循了A的代理,而且实现了A的代理方法。
dataSource:表示数据源,若是A对象声明了数据源,当咱们建立A对象的时候,咱们就该实现数据源,来告诉A,他所须要的一些数据。例如:tableView数据源方法,须要告诉它,我要实现几组cell,每组cell多少行cell,实现的cell什么样式,什么内容
一样delegate
和dataSource
,都是可使用require
和optional
来修饰的。面试
相同点:代理和Block大可能是咱们均可以用来作倒序传值的。咱们都得注意避免循环引用。
不一样点:代理使用weak修饰,代理必须先声明方法。当咱们调用代理的时候要判断是否已经实现。
block:使用的是copy来修饰,block保存的是一段代码,其实也就是一个函数。当咱们调用block的时候要判断是否已经实现。sql
属性是描述类的特征,也就是具有什么特性。三个部分,带下划线的成员变量,get、setter方法。
默认关键字:readwrite,assign, atomic;
@dynamic :修饰的属性,其getter和setter方法编译器是不会自动帮你生成。必须本身是实现的。
@synthesize:修饰的属性,其getter和setter方法编译器是会自动帮你生成。没必要本身实现,能够指定与属性相对应的成员变量。数据库
默认关键字:readwrite,assign, atomic;数组
众所周知,咱们知道,可变类型(NSMutableArray,NSMutableString等)是不可边类型(NSString,NSArray等)的子类,由于多态的缘由,咱们可使用赋值指向子类对象,也就是咱们可使用不可边类型去接受可变类型。
1.当咱们使用strong修饰A不可边类型的时候,而且使用B可变类型给A赋值,再去修改可变类型B值的时候,A所指向的值也会发生改变。引文strong只是让建立的对象引用计数器+1,并返回当前对象的内容地址,当咱们修改B指向的内容的时候,A指向的内容也一样发生了改变,由于他们指向的内存地址是相同的,是一分内容。
2.当咱们使用copy修饰A不可边类型的时候,而且使用B可变类型给A赋值,再去修改可变类型B值的时候,A所指向的值不会发生改变。由于当时用copy的修饰的时候,会拷贝一分内容出来,而且返回指针给A,当咱们修改B指向的内容的时候,A指向的内容是没有发生改变的。由于A指向的内存地址和B指向的内存地址是不相同的,是两分内容
3.copy修饰不可边类型(NSString,NSArray等)的时候,且使用不可边类型进行赋值,表示浅拷贝,只拷贝一份指针,和strong修饰同样,当修饰的是可变类型(NSMutableArray,NSMutableString等)的时候,表示深拷贝,直接拷贝新一分内容,到内存中。表示两分内容。缓存
必须遵循
nscopying
协议,若是想实现可变和不可边拷贝时,必须同时遵循nscoping
和nsmutablecoping
协议。而且实现
- (id)copyWithZone:(NSZone *)zone;安全
可变使用copy表示深拷贝,不可变集合类使用copy的时候是浅拷贝。
可变集合类使用mutablecopy表示深拷贝,不可变集合类使用copy的时候是浅拷贝。
关于容器实现copy 或 metableCopy ,容器内元素默认都是 指针拷贝,不是内容复制。网络
在xib或者Sb拖控件时,其实控件就加载到了父控件的subviews数组里面,进行了强引用,即便使用了weak,也不形成对象的释放。
nonatomic:表示非原子,不安全,可是效率高。
atomic:表示原子行,安全,可是效率定。
atomic:不能绝对保证线程的安全,当多线程同时访问的时候,会形成线程不安全。可使用线程锁来保证线程的安全。
实现一个自定义layout的常规作法是继承UICollectionViewLayout类,而后重载下列方法:
-(CGSize)collectionViewContentSize 返回collectionView的内容的尺寸 -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 返回rect中的全部的元素的布局属性 返回的是包含UICollectionViewLayoutAttributes的NSArray UICollectionViewLayoutAttributes能够是cell,追加视图或装饰视 图的信息,经过不一样的UICollectionViewLayoutAttributes初始化方法能够获得不一样类型的UICollectionViewLayoutAttributes: layoutAttributesForCellWithIndexPath: layoutAttributesForSupplementaryViewOfKind:withIndexPath: layoutAttributesForDecorationViewOfKind:withIndexPath: -(UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath 返回对应于indexPath的位置的cell的布局属性 -(UICollectionViewLayoutAttributes )layoutAttributesForSupplementaryViewOfKind:(NSString )kind atIndexPath:(NSIndexPath *)indexPath 返回对应于indexPath的位置的追加视图的布局属性,若是没有追加视图可不重载 -(UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString)decorationViewKind atIndexPath:(NSIndexPath )indexPath 返回对应于indexPath的位置的装饰视图的布局属性,若是没有装饰视图可不重载 -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 当边界发生改变时,是否应该刷新布局。若是YES则在边界变化(通常是scroll到其余地方)时,将从新计算须要的布局信息。
使用简单逻辑页面的跳转是可使用sb的,开发比较块。
可是SB对于逻辑项目比较复杂的时候,开发起来比较慢。不适合多人合做开发;也不利于版本的梗系和后期的维护。使用sb在项目变异编译的时候,也都会直接加载到内存中,形成内存的浪费。
可使用xib来代替,编辑复杂逻辑界面时候可使用纯码编写。
进程:是具备必定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程本身基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),可是它可与同属一个进程的其余的线程共享进程所拥有的所有资源.
同步:阻塞当前线程操做,不能开辟线程。
异步:不阻碍线程继续操做,能够开辟线程来执行任务。
并发:当有多个线程在操做时,若是系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分红若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式咱们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操做有可能非并发。当一个CPU执行一个线程时,另外一个CPU能够执行另外一个线程,两个线程互不抢占CPU资源,能够同时进行,这种方式咱们称之为并行(Parallel)。
区别:并发和并行是即类似又有区别的两个概念,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。假若在计算机系统中有多个处理机,则这些能够并发执行的程序即可被分配到多个处理机上,实现并行执行,即利用每一个处理机来处理一个可并发执行的程序,这样,多个程序即可以同时执行。
当使用dispath-async函数开辟线程执行任务的完成时,咱们须要使用dispatch_async(dispatch_get_main_queue(), ^{ });函数会到主线程内刷新UI。并完成通讯
咱们使用队列组来开辟线程时,队列组中的队列任务是并发,当全部的队列组中的全部任务完成时候,才能够调用队列组完成任务。
/**建立本身的队列*/ dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT); /**建立一个队列组*/ dispatch_group_t dispatchGroup = dispatch_group_create(); /**将队列任务添加到队列组中*/ dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-1"); }); /**将队列任务添加到队列组中*/ dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dspatch-2"); }); /**队列组完成调用函数*/ dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){ NSLog(@"end"); })
barrier:表示栅栏,当在并发队列里面使用栅栏时候,栅栏以前的并发任务开始并发执行,执行完毕后,执行栅栏内的任务,等栅栏任务执行完毕后,再并发执行栅栏后的任务。
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-1"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-2"); }); dispatch_barrier_async(concurrentQueue, ^(){ NSLog(@"dispatch-barrier"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-3"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-4"); });
信号量:Semaphore是经过‘计数’的方式来标识线程是不是等待或继续执行的。信号量
dispatch_semaphore_create(int) // 建立一个信号,并初始化信号的计数大小 /* 等待信号,而且判断信号量,若是信号量计数大于等于你建立时候的信号量的计数,就能够经过,继续执行,而且将你传入的信号计数减1, * 若是传入的信号计数小于你建立的计数,就表示等待,等待信号计数的变化 * 若是等待的时间超过你传入的时间,也会继续下面操做 * 第一个参数:semaphore 表示信号量 * 第二个参数:表示等待的时间 * 返回int 若是传入的信号计数大于等于你建立信号的计数时候,返回0. 反之,返回的不等于0 */ int result = dispatch_semaphore_wait(dispatch_semaphore_t semaphore,time outTime);// 表示等待,也是阻碍线程 // 表示将信号技术+1 dispatch_semaphore_signl(dispatch_semaphore_t semaphore);
实现线程的同步的方法:串行队列,分组,信号量。也是可使用并发队列。
//加入队列 dispatch_async(concurrentQueue, ^{ //1.先去网上下载图片 dispatch_sync(concurrentQueue, ^{ }); //2.在主线程展现到界面里 dispatch_sync(dispatch_get_main_queue(), ^{ }); });
当咱们使用多线程来访问同一个数据的时候,就有可能形成数据的不许确性。这个时候我么可使用线程锁的来来绑定。也是可使用串行队列来完成。如:fmdb就是使用FMDatabaseQueue,来解决多线程抢夺资源。
持久化方案:
plist,存储字典,数组比较好用
preference:偏好设置,实质也是plist
NSKeyedArchiver:归档,能够存储对象
sqlite:数据库,常用第三方来操做,也就是fmdb
coreData:也是数据库储存,苹果官方的
1.nscache 是能够自动释放内存的。
2.nscache是线程安全的,咱们能够在不一样的线程中添加,删除和查询缓存中的对象。
3.一个缓存对象不会拷贝key对象。
我的理解:初始化函数,若是你想自定义初始化函数时,也是必需要初始化父类,以来保证能够继承父类的一些方法或者属性。
Designated Initializer
description是nsobject的一个实例的方法,返回的是一个nsstring。当咱们使用nslog打印的时候,打印出来的通常都是对象的内存地址,若是咱们实现description方法时,咱们就可使用nslog打印对象的时候,咱们能够把它里面的属性值和内存地址一块儿打印出来.打印什么,就是看你写什么了。
-(NSString *)description{ NSString * string = [NSString stringWithFormat:@"<Person:内存地址:%p name = %@ age = %ld>",self,self.name,self.age]; return string; }
使用内存管理计数器,来管理内存的。当内存管理计数器为0的时候,对象就会被释放。
block:本质就是一个object-c对象.
block:存储位置,可能分为3个地方:代码去,堆区、栈区(ARC状况下会自动拷贝到堆区,所以ARC下只能有两个地方:代码去、堆区)
代码区:不访问栈区的变量(如局部变量),且不访问堆区的变量(alloc建立的对象),此时block存放在代码去。
堆区:访问了处于栈区的变量,或者堆区的变量,此时block存放在堆区。–须要注意实际是放在栈区,在ARC状况下会自动拷贝到堆区,若是不是ARC则存放在栈区,所在函数执行完毕就回释放,想再外面调用须要用copy指向它,这样就拷贝到了堆区,strong属性不会拷贝、会形成野指针错区。
默认状况下,block里面的变量,拷贝进去的是变量的值,而不是指向变量的内存的指针。
当使用__block
修饰后的变量,拷贝到block
里面的就是指向变量的指针,因此咱们就能够修改变量的值。
Person *p = [[Person alloc]init]; [p setPersonBlock:^(NSString *str) { p.name = str; }];
根据对象的
isa
指针找到类对象id
,在查询类对象里面的methodLists
方法函数列表,若是没有在好到,在沿着superClass
,寻找父类,再在父类methodLists
方法列表里面查询,最终找到SEL
,根据id
和SEL
确认IMP
(指针函数),在发送消息;
当发送消息的时候,咱们会根据类里面的
methodLists
列表去查询咱们要动用的SEL
,当查询不到的时候,咱们会一直沿着父类查询,当最终查询不到的时候咱们会报unrecognized selector错误
当系统查询不到方法的时候,会调用+(BOOL)resolveInstanceMethod:(SEL)sel
动态解释的方法来给我一次机会来添加,调用不到的方法。或者咱们能够再次使用-(id)forwardingTargetForSelector:(SEL)aSelector
重定向的方法来告诉系统,该调用什么方法,一来保证不会崩溃。
1.不能向编译后获得的类增长实例变量
2.能向运行时建立的类中添加实例变量
解释:
1.编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经肯定,runtime会调用class_setvarlayout或class_setWeaklvarLayout来处理strong weak引用.因此不能向存在的类中添加实例变量
2.运行时建立的类是能够添加实例变量,调用class_addIvar函数.可是的在调用objc_allocateClassPair以后,objc_registerClassPair以前,缘由同上.
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址做为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到全部以a为键的 weak 对象,从而设置为 nil。
instance_size :实例的内存大小
objc_ivar_list *ivars:属性列表
runloop:字面意思就是跑圈,其实也就是一个循环跑圈,用来处理线程里面的事件和消息。
runloop和线程的关系:每一个线程若是想继续运行,不被释放,就必须有一个runloop来不停的跑圈,以来处理线程里面的各个事件和消息。
主线程默认是开启一个runloop。也就是这个runloop才能保证咱们程序正常的运行。子线程是默认没有开始runloop的
model:是runloop里面的模式,不一样的模式下的runloop处理的事件和消息有必定的差异。
系统默认注册了5个Mode:
(1)kCFRunLoopDefaultMode: App的默认 Mode,一般主线程是在这个 Mode 下运行的。
(2)UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其余 Mode 影响。
(3)UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就再也不使用。
(4)GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,一般用不到。
(5)kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际做用。
注意iOS 对以上5中model进行了封装
NSDefaultRunLoopMode;
NSRunLoopCommonModes
nstime对象是在
NSDefaultRunLoopMode
下面调用消息的,可是当咱们滑动scrollview的时候,NSDefaultRunLoopMode
模式就自动切换到UITrackingRunLoopMode
模式下面,却不能够继续响应nstime发送的消息。因此若是想在滑动scrollview的状况下面还调用nstime的消息,咱们能够把nsrunloop的模式更改成NSRunLoopCommonModes
Autorelease Pool做用:缓存池,能够避免咱们常常写relase的一种方式。其实就是延迟release,将建立的对象,添加到最近的autoreleasePool中,等到autoreleasePool做用域结束的时候,会将里面全部的对象的引用计数器-1.
autorelease
在oc中,类也是对象,所属元类。因此常常说:
万物皆对象
对象的isa指针指向所属的类
类的isa指针指向了所属的元类
元类的isa指向了根元类,根元类指向了本身。
调用的方式不一样,类方法必须使用类调用,在方法里面不能调用属性,类方法里面也必须调用类方法。存储在元类结构体里面的
methodLists
里面
实例方法必须使用实例对象调用,能够在实例方法里面使用属性,实例方法也必须调用实例方法。存储在类结构体里面的methodLists
里面
category:咱们能够给类或者系统类添加实例方法方法。咱们添加的实例方法,会被动态的添加到类结构里面的
methodList
列表里面。categort
能够添加属性的,但必须咱们实现它的
getter
和setter
方法。可是没有添加带下滑线同名的成员变量
可是咱们使用runtime
咱们就能够实现添加成员变量方法以下
- (void)setName:(NSString *)name { /** * 为某个类关联某个对象 * * @param object#> 要关联的对象 description#> * @param key#> 要关联的属性key description#> * @param value#> 你要关联的属性 description#> * @param policy#> 添加的成员变量的修饰符 description#> */ objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)name { /** * 获取到某个类的某个关联对象 * * @param object#> 关联的对象 description#> * @param key#> 属性的key值 description#> */ return objc_getAssociatedObject(self, @selector(name)); }
• 若是一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil)。例如:Person * motherInlaw = [ aPerson spouse] mother]; 若是spouse对象为nil,那么发送给nil的消息mother也将返回nil。
• 若是方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者long long的整型标量,发送给nil的消息将返回0。
• 若是方法返回值为结构体,正如在《Mac OS X ABI 函数调用指南》,发送给nil的消息将返回0。结构体中各个字段的值将都是0。其余的结构体数据类型将不是用0填充的。
• 若是方法的返回值不是上述提到的几种状况,那么发送给nil的消息的返回值将是未定义的。
缓存高度:当咱们建立frame模型的时候,计算出来cell的高度的时候,咱们能够将cell的高度缓存到字典里面,以cell的
indexpath
和Identifier
做为为key。
NSString *key = [[HeightCache shareHeightCache] makeKeyWithIdentifier:@"YwywProductGradeCell" indexPath:indexPath]; if ([[HeightCache shareHeightCache] existInCacheByKey:key]) { return [[HeightCache shareHeightCache] heightFromCacheWithKey:key]; }else{ YwywProductGradeModelFrame *modelFrame = self.gradeArray[indexPath.row]; [[HeightCache shareHeightCache] cacheHieght:modelFrame.cellHight key:key]; return modelFrame.cellHight; }
异步绘制、减小层级:目前还不是很清楚
hide:我的理解应该是hidden
吧,把可能会用到的控件都建立出来,根据不一样的状况去隐藏或者显示出来。
避免离屏渲染:只要不是同时使用边框/边框颜色以及圆角的时候
,均可以使用layer直接设置。不会形成离屏渲染。
交换方式:通常写在类的+(void)load方法里面
/** 获取原始setBackgroundColor方法 */ Method originalM = class_getInstanceMethod([self class], @selector(setBackgroundColor:)); /** 获取自定义的pb_setBackgroundColor方法 */ Method exchangeM = class_getInstanceMethod([self class], @selector(pb_setBackgroundColor:)); /** 交换方法 */ method_exchangeImplementations(originalM, exchangeM);
Class MyClass = objc_allocateClassPair([NSObject class], "Person", 0);
/**参数1、类名参数 2、SEL 添加的方法名字参数 3、IMP指针 (IMP就是Implementation的缩写,它是指向一个方法实现的指针,每个方法都有一个对应的IMP) 参数4、其中types参数为"i@:@“,按顺序分别表示:具体类型可参照[官方文档](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)i 返回值类型int,如果v则表示void@ 参数id(self): SEL(_cmd)@ id(str) V@:表示返回值是void 带有SEL参数 (An object (whether statically typed or typed id)) */ class_addMethod(Person, @selector(addMethodForMyClass:), (IMP)addMethodForMyClass, "V@:");
/**参数1、类名参数 2、属性名称参数 3、开辟字节长度参数 4、对其方式参数 5、参数类型 “@” 官方解释 An object (whether statically typed or typed id) (对象 静态类型或者id类型) 具体类型可参照[官方文档](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)return: BOOL 是否添加成功 */ BOOL isSuccess = class_addIvar(Person, "name", sizeof(NSString *), 0, "@"); isSuccess?NSLog(@"添加变量成功"):NSLog(@"添加变量失败");
sd加载一张图片的时候,会先在内存里面查找是否有这张图片,若是没有会根据图片的md5(url)后的名称去沙盒里面去寻找,是否有这张图片,若是没有会开辟线程去下载,下载完毕后加载到imageview上面,并md(url)为名称缓存到沙盒里面。
AFN 目的:就是开辟线程请求网络数据。若是没有常住线程的话,就会每次请求网络就去开辟线程,完成以后销毁开辟线程,这样就形成资源的浪费,开辟一条常住线程,就能够避免这种浪费,咱们能够在每次的网络请求都添加到这条线程。
kvo:键值观察,根据键对应的值的变化,来调用方法。
注册观察者:addObserver:forKeyPath:options:context:
实现观察者:observeValueForKeyPath:ofObject:change:context:
移除观察者:removeObserver:forKeyPath:(对象销毁,必须移除观察者)
注意
使用kvo监听A对象的时候,监听的本质不是这个A对象,而是系统建立的一个中间对象NSKVONotifying_A
并继承A对象,而且A对象的isa指针指向的也不是A的类,而是这个NSKVONotifying_A
对象
kvo详解
kvo详解2
kvc:键值赋值,使用最多的即便字典转模型。利用runtime获取对象的全部成员变量, 在根据kvc键值赋值,进行字典转模型
setValue: forKey: 只查找本类里面的属性
setValue: forKeyPath:会查找本类里面属性,没有会继续查找父类里面属性。
kvc详解
以上就是所有的面试题,后面的面试题,就该根据我的的状况来回答了。
以上的面试回答,有的是根据本身的知识储备回答,有的也是小编百度的。
以上的回答也不知道对与错,欢迎你们发表本身的见解
做者:七秒记忆的鱼儿连接:http://www.jianshu.com/p/f9eb6b315c08來源:简书著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。