1. 内存中的栈和堆的区别是什么?那些数据在栈上,哪些在堆上?
对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来讲,释放工作有程序员控制,容易产生memory Leak。
栈的存储顺序是有高地址向低地址 是一块连续的内存区域。能获得栈的空间较小 栈有两种分配方式:静态分配和动态分配
堆的存储循序是有低地址向高地址 是不连续的内存区域 堆获得的空间比较灵活,也比较大。堆是动态分配和回收内存的,没
有静态分配的堆
2. 什么情况下会出现内存的循环引用block中的weak self,是任何时候都需要加的么?
引发循环引用,是因为当前self在强引用着block,而block又引用着self,这样就造成了循环引用。而需不需要使用[weak self]就
是由循环引用来决定,如果造成了循环引用,就必须使用[weak self]来打破循环.
3. GCD的queue,main queue中执行的代码,一定是在main thread么?
对于queue中所执行的代码不一定在main thread中。如果queue是在主线程中创建的,那么所执行的代码就是在主线程中执
行。如果是在子线程中创建的,那么就不会在main thread中执行。
对于main queue就是在主线程中的,因此一定会在主线程中执行。获取main queue就可以了,不需要我们创建,获取方式通
过调用方法dispatchgetmain_queue来获取。
4..h文件中的变量,外部可以直接访问么?(注意是变量,不是property)?
直接访问的话是没法访问到的,可以通过runtime的ivar去获取他的所有变量信息遍历就可以获取到
5. NSThread中的Runloop的作用,如何使用?
NSThread+runloop实现常驻线程
首先常驻线程既然是常驻,那么我们可以用GCD实现一个单例来保存NSThread
+ (NSThread *)shareThread {
static NSThread *shareThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest2) object:nil];
[shareThread setName:@"threadTest"];
[shareThread start];
});
return shareThread;
}
+ (void)threadTest
{
@autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];}}
[self performSelector:@selector(test) onThread:[ViewController shareThread] withObject:nil waitUntilDone:NO];
- (void)test
{ NSLog(@"test:%@", [NSThread currentThread]);}
6. NSOperationQueue有哪些使用方式?
NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也
更高。
可以添加操作之间的依赖关系,方便的控制执行顺序。
可以设定操作执行的优先级。
可以可以很方便的取消一个操作的执行。
使用 KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled。
NSOperation 子类NSInvocationOperation、NSBlockOperation,或者自定义子类来封装操作。
NSOperation 实现多线程的使用步骤分为三步:
创建操作:先将需要执行的操作封装到一个 NSOperation 对象中。
创建队列:创建 NSOperationQueue 对象。
将操作加入到队列中:将 NSOperation 对象添加到 NSOperationQueue 对象中。
使用子类 NSBlockOperation,并调用方法 AddExecutionBlock:的情况下,blockOperationWithBlock:方法中的操作
和 addExecutionBlock:中的操作是在不同的线程中异步执行的。而且,这次执行结果中 blockOperationWithBlock:方法中的操
作也不是在当前线程(主线程)中执行的。从而印证了blockOperationWithBlock:中的操作也可能会在其他线程(非当前线程)
中执行。
queue addOperation:op3];
无需先创建操作,在 block 中添加操作,直接将包含操作的 block 加入到队列中。
- (void)addOperationWithBlock:(void (^)(void))block;
最大并发操作数:maxConcurrentOperationCount
NSOperation操作依赖:
- (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。
NSOperation优先级:
/ 优先级的取值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
NSOperation 常用属性和方法:
取消操作方法
- (void)cancel;可取消操作,实质是标记 isCancelled 状态。
判断操作状态方法
- (BOOL)isFinished;判断操作是否已经结束。
- (BOOL)isCancelled;判断操作是否已经标记为取消。
- (BOOL)isExecuting;判断操作是否正在在运行。
- (BOOL)isReady;判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
操作同步
- (void)waitUntilFinished;阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
- (void)setCompletionBlock:(void (^)(void))block;completionBlock会在当前操作执行完毕时执行 completionBlock。
- (void)addDependency:(NSOperation *)op;添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op;移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies;在当前操作开始执行之前完成执行的所有操作对象数组。
NSOperationQueue常用属性和方法:
取消/暂停/恢复操作
- (void)cancelAllOperations;可以取消队列的所有操作。
- (BOOL)isSuspended;判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
- (void)setSuspended:(BOOL)b;可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
操作同步
- (void)waitUntilAllOperationsAreFinished;阻塞当前线程,直到队列中的操作全部执行完毕。
添加/获取操作
- (void)addOperationWithBlock:(void (^)(void))block;向队列中添加一个 NSBlockOperation 类型操作对象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
- (NSArray *)operations;当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
- (NSUInteger)operationCount;当前队列中的操作数。
获取队列
+ (id)currentQueue;获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
+ (id)mainQueue;获取主队列。
7. UIButton的父类是什么?UILabel呢?
UIButton是继承于UIControl,并且也遵循NSCoding的协议
UILabel是继承于UIView,并且也遵循NSCoding的协议;
8. #define和const定义的变量,有什么区别?
1. define是宏定义,程序在预处理阶段将用define定义的内容进行了替换。因此程序运行时,常量表中并没有用define定义的常量,系统不为它分配内存。
const 定义的常量,在程序运行时在常量表中,系统为它分配内存。
2.define 定义的常量,预处理时只是直接进行了替换。所以编译时不能进行数据类型检验。
const 定义的常量,在编译时进行严格的类型检验,可以避免出错。
3.define定义表达式时要注意“边缘效应”
9. Struct 和 Class 有什么区别?
class有这几个功能struct没有的:
class可以继承,这样子类可以使用父类的特性和方法
类型转换可以在runtime的时候检查和解释一个实例的类型
一个类可以被多次引用
struct也有这样几个优势:
结构较小,适用于复制操作,相比于一个class的实例被多次引用更加安全
10. lass A 继承 class B,class B 继承 NSObject。画出完整的类图。
11. pushViewController和presentViewController的区别?
从源码可以看出来,pushViewController是作用于navigationController 的。也就是说,如果想要使用pushViewController来进行
界面的跳转,就必须保证当前页面有导航栏(navigationController )。至于presentViewController,就没有这个限制条件了,
在当前页面没有导航栏的时候,也可以使用.
还有,pushViewController是把一个视图压入栈,然后显示出来,这样可以方便查找之前的视图,能够直接退回到之前的任意一个视图。而presentViewController是直接显示一个视图,这样每次就只能退回到前面的那一个视图
而且,在一直使用pushViewController的导航之间,一旦使用了presentViewController,当前界面的导航栏
(navigationController )就会变成空的,那么pushViewController也就没办法使用了。这时,如果想要再次使用
pushViewController,就必须重新设置导航栏(navigationControlle.
12. 手写一个枚举?
typedef NS_ENUM(NSInteger, CYLSex) {
CYLSexMan,
CYLSexWoman
};
13. Category 中有 load 方法吗?load 方法是什么时候调用的?load 方法能继承吗?
在运行时时期, 会将 Category 中的实例方法列表, 协议列表, 属性列表添加到主类中, 并且不会对 load 方法做特殊处理, 故 load
方法跟其它方法一样, 被插到主类中.
父类的 load 方法先调用
主类中的 load 方法先调用, 分类中的 load 方法后调用
分类之间的 load 方法调用顺序, 看文件编译的顺序
在运行时时期, 循环调用所有类的 +load 方法. 直接使用函数内存地址的方式 (*load_method)(cls, SEL_load); 而不是使用发送消
息 objc_msgSend 的方式.
14. layoutIfNeeded和setNeedsLayout区别?
layoutIfNeeded方法一被调用,主线程会立即强制重新布局,它会从当前视图开始,一直到完成所有子视图的布局
setNeedsLayout 和layoutIfNeeded相似,唯一不同的是他不会立即强制视图重新布局,而是在下一个布局周期才会触发更新.他主
要用于多个视图布局先后更新的场景;
15. 谓词的认识?
NSPredicate类是用来定义逻辑条件约束的获取或内存中的过滤搜索
例子1:NSArray*array=@[@1,@2,@3,@4,@5,@6,@7];
NSPredicate*predicate=[NSPredicate predicateWithFormat:@"self > 2 && self < 5"];
NSArray*filterArray=[array filteredArrayUsingPredicate:predicate];
//filterArray is @[@3,@4];
例子2:
NSNumber *number = @123;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self = 123"];
BOOL result = [predicate evaluateWithObject:number];
例子三:
NSNumber *number = @123;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self between {123, 200}"];
BOOL result = [predicate evaluateWithObject:number];
16. Autolayout有遇到性能问题吗?
AutoLayout,一个基于约束布局,动态计算视图大小和位置的库。性能肯定比固定计算的高度慢
17. 如何通过一个view查找它所在的viewController?
在UIKit中,UIApplication、UIView、UIViewController这几个类都是直接继承自UIResponder类。
id responder = self.nextResponder;
while (![responder isKindOfClass: [UIViewController class]] && ![responder isKindOfClass: [UIWindow class]])
{
responder = [responder nextResponder];
}
if ([responder isKindOfClass: [UIViewController class]])
{
// responder就是view所在的控制器
// do something
}
18. 内存泄露的原因及查找?
内存泄露( memory leak):是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露
堆积后果很严重,无论多少内存,迟早会被占光。
静态内存泄漏分析方法:
通过xcode打开项目,然后点击product-->Analyze
静态分析方法能发现大部分的问题,但是只能是静态分析结果,有一些并不准确,还有一些动态分配内存的情形并没有进行分
析
动态分析方法:
分析内存泄露不能把所有的内存泄露查出来,有的内存泄露是在运行时,用户操作时才产生的。那就需要用到Instruments了。
具体操作是通过xcode打开项目,然后点击product-->profile 按上面操作,build成功后跳出Instruments工具,如上图右侧图所
示。选择Leaks选项
选中Leaks Checks,在Details所在栏中选择CallTree,并且在右下角勾选Invert Call Tree 和Hide System Libraries,会发现显示若
干行代码,双击即可跳转到出现内存泄漏的地方,修改即可
内存泄露原因:
在目前主要以ARC进行内存管理的开发模式,导致内存泄漏的主要原因是代码中存在循环引用
ViewController中存在NSTimer
ViewController中的代理delegate
ViewController中Block
非OC对象内存处理
加载大图片或者多个图片
地图类
NSNotification的观察者忘记移除
ViewController的子视图对self的持有
.这个问题也是我的项目中内存泄漏的问题所在。我们有时候需要在子视图或者某个cell中点击跳转等操作,需要在子视图或cell中持有当前的ViewController对象,这样跳转之后的back键才能直接返回该页面,同时也不销毁当前ViewController。此时,你就要注意在子视图或者cell中对当前页面的持有对象不能是强引用,尽量assign或者weak,否则会造成循环引用,内存无法释放。
19. 如何扩大view的响应范围?
重写view的下面方法可以实现
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event;
的这个方法就可以实现。其实这个方法就是传个你点击的点 然后你去判断这个点是否在视图上。
@interface UIView (ChangeScope)
- (void)changeViewScope:(UIEdgeInsets)changeInsets;
@end
在这个分类的实现中:
#import "UIView+ChangeScope.h"
#import <objc/runtime.h>
@implementation UIView (ChangeScope)
static char *changeScopeKey;
- (void)setChangeScope:(NSString *)changeScope
{
objc_setAssociatedObject(self, &changeScopeKey, changeScope, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)changeScope
{
return objc_getAssociatedObject(self, &changeScopeKey);
}
- (void)changeViewScope:(UIEdgeInsets)changeInsets
{
self.changeScope = NSStringFromUIEdgeInsets(changeInsets);
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
UIEdgeInsets changeInsets = UIEdgeInsetsFromString(self.changeScope);
if (changeInsets.left != 0 || changeInsets.top != 0 || changeInsets.right != 0 || changeInsets.bottom != 0) {
CGRect myBounds = self.bounds;
myBounds.origin.x = myBounds.origin.x + changeInsets.left;
myBounds.origin.y = myBounds.origin.y + changeInsets.top;
myBounds.size.width = myBounds.size.width - changeInsets.left - changeInsets.right;
myBounds.size.height = myBounds.size.height - changeInsets.top - changeInsets.bottom;
return CGRectContainsPoint(myBounds, point);
} else {
return CGRectContainsPoint(self.bounds,point);
}
}
@end
或者
解决方案:
重写一个Button类,这个button类继承与UIButton,
重写- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event, 作用:判断下传入过来的点在不在方法调用者的坐标系上
// 作用:判断下传入过来的点在不在方法调用者的坐标系上
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
CGRect bounds =CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
//宽高希望扩展的范围
CGFloat widthDelta =30;
CGFloat heightDelta =20;
//相当于bounds 上下左右都增加了10的额外
bounds =CGRectInset(bounds, -0.5*widthDelta, -0.5* heightDelta);//注意这里是负数,扩大了之前的bounds的范围
//点击的点是否在这个范围
return CGRectContainsPoint(bounds, point);
}
20.字典的实现原理?
字典通过使用- (void)setObject:(id)anObject forKey:(id)aKey;方法,用Hash表来实现key和value之间映射和存储的。
哈希概念:哈希表本质是一个数组,每一个元素称为一个箱子,而箱子里面的存放的是键值对。
哈希表(Hash Table)也叫做散列表,这是根据关键码(key vlaue)而直接进行访问的数据结构。换句话说,通过把关键码映
射到表里面的一个位置来访问记录,用来加快查找的速度。映射的函数就叫做散列函数,存放记录的数组也叫做散列表。
哈希存储过程
3.1 首先根据key计算出它的哈希值h
3.2 如果箱子的个数为n,那么key值是应该放在第(h%n)个箱子中
3.3 如果该箱子已经有了值,就使用开放寻址法或者拉链法进行解决冲突。
如果我们在使用拉链法解决哈希冲突时候,每个箱子都是一个链表,属于同一个箱子的所有键值都会排列在链表中。
网友评论