1、为什么说OC是一门动态语言?
主要体现以下三个方面: 动态类型、动态绑定
(1) 动态类型 : 即运行时再决定对象的类型。简单说就是id类型,任何对象都可以被id指针所指,只有在运行时才能决定是什么类型。
例如 : `NSString *string = [[NSData alloc]init];`
编译时 是 NSString 类型,运行时 却是 NSData 类型
(2) 动态绑定 :基于动态类型,在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,这就是动态绑定。
例如 :我们一般向一个NSObject对象发送-respondsToSelector:或者 -instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类 的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态地向类或者实例添加新的方 法,也即类的实现是可以动态绑定的。
2、frame和bouns的区别
frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)
bounds指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)
例如:view的frame是{10,10,50,50},bounds就是{0,0,50,50};
bounds可理解成边界
3、UIView和CALayer的区别和联系
(1) UIView主要用来响应事件,CALayer不可以
原因:UIView继承UIResponder(在UIResponder中定义了处理各种事件和事件传递的接口),CALayer继承NSObject
(2) UIView是CALayer的代理对象,实现其代理方法drawRect,从而绘制出了 UIView 的内容,CALayer比UIView多了个AnchorPoin(锚点)
(3) 两者都有树状层级结构,layer 内部有 SubLayers,View 内部有 SubViews
4、+ (void)load
和 +(void)initialize
的区别
(1) 二者都是定义在NSObject中的静态方法
(2) 在不考虑主动调用的情况下,二者默认都只会被调用一次。
load 方法会在加载类的时候就被调用(在main方法前),initialize在类的方法第一次被调时执行(main方法后)
(3)load方法是在程序启动之前就调用,父类先于子类执行,类先于分类执行
(4)load经常被用来做交换IMP操作,initialize用于初始化静态变量
5、import、include、@class区别。import"" 和import<>的区别
(1) #import和#include都能完整地包含某个文件的内容,#import能防止同一个文件被包含多次
(2) @class仅仅是声明一个类名,并不会包含类的完整声明;@class还能解决循环包含的问题
(3)#import <> 用来包含系统自带的文件,#import “”用来包含自定义的文件
备注: @class 只是声明,例如,我用block回调一类型变量出来,我可能会调用变量的某个方法。例如,简单的removeSuperView,这时候 Receiver type ‘’ for instance message is a forward报错。就知道应该要import。
6、OC中的数字对象都有哪些,与基本数据类型的区别是什么
oc中用NSNumber类来包装基本数据类型,基本类型只是一个值,没有任何行为。
**7、weak 和 assign 的区别 **
(1) weak 和 assign 都是弱引用声明类型
(2) weak只能在ARC下使用,只能修饰对象
(3) ARC下,指针变量一定要用weak修饰,
只有基本数据类型和结构体需要用assgin
(3) ARC下,如果用weak声明的变量在栈中就会自动清空,赋值为nil。
如果用assign声明的变量在栈中可能不会自动赋值为nil,
就会造成野指针错误!
(4)weak使用规则:只要还有一个变量指向对象,对象就会保持在内存中
8、Category 和 Extension 的区别
Category的作用 :
(1)category的主要作用是为已经存在的类添加方法,不能扩充成员变量,
如果要空充变量,可以使用runtime动态添加
objc_setAssociatedObject 和 objc_getAssociatedObject 完成
(2) 如果category中的方法和类中原有方法同名,运行时会优先调用category中的方法。
这也就是我们平常所说的category的方法会“覆盖”
掉原来类的同名方法,这是因为运行时在查找方法的时候是
顺着方法列表的顺序查找的,它只要一找到对应名字的方法,
就会罢休,殊不知后面可能还有一样名字的方法。
Extenison的作用 :
(1)能为某个类添加成员变量,属性,方法;
(2)一般的类扩展写到.m文件中;
(3)一般的私有属性写到类扩展中
区别 :
(1) category 是在编译阶段被添加到类中,而Extenison是在运行时添加到类中
(2) 类扩展不能像类别那样拥有独立的实现部分
即 类扩展所声明的方法必须依托对应类的实现部分来实现。
(3) 类扩展不仅可以增加方法,还可以增加实例变量(或者属性),只是该实例变量默认是@private类型的
(4)定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有的。类扩展是在 .m 文件中声明私有方法的非常好的方式。
注意 : 我自己的理解,extension直接理解成放在实现文件的.h文件。
9、关于 深拷贝 和 浅拷贝
浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器+1;(人和影子,互相影响)
深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变;(克隆人,互不影响)
常见的笔试题目:
@property (nonatomic, copy) NSMutableArray *mutableArray;
NSMutableArray *array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];
细节 : 对可变对象进行copy得到不可变对象,例如,可变数组放对象,
copy得到一个不可变数组。当对数组里对象进行修改,可变和不可变数组的对象皆修改。
也就是说,数组中的对象并没有实现真正的拷贝,始终是引用拷贝。Mutablecopy只不过新开辟内存空间而已,对象里面的数据依然是指针复制的。
// copy 性质的setter内部实现
-(void)setmutableArray:(NSMutableArray *)mutableArray
{
if (_mutableArray != mutableArray)
{
[_mutableArray release];
_mutableArray = [mutableArray copy];
}
}
10、如何写一个完整的单例
static ShareInstance *instance = nil;
+ (id)ShareInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc]init];
});
return instance;
}//静态方法
+ (instancetype)allocWithZone:(NSZone *)zone
{
return [self ShareInstance];
}
+ (id)copyWithZone:(struct _NSZone *)zone
{
return [self ShareInstance];
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone
{
return [self ShareInstance];
}
//MRC 增加如下方法
-(oneway void)release
{
}
-(instancetype)retain
{
return instance;
}
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
11、简述内存管理,autorelease的作用,及autoreleasepool的对象什么时候释放。
内存管理黄金法则 : 谁创建,谁释放。谁引用,谁管理。
OC是通过引用计数管理对象的声明周期,当对象通过 new、alloc、retain、拷贝 持有对象的时候,对应对应引用计数+1.那么就有义务对当对象不被使用的情况下release操作,使其引用计数-1。当对应引用计数为0的时候,对象生命周期结束。
autorelease的作用是声明将对象放入自动释放池。
当程序处于休眠状态的时候,autoreleasepool会向池中的对象分别发送release消息,对象引用计数-1。当对象的引用计数为0的时候,对象销毁。
12、SDWebImage 如何识别文件格式。
+ (NSString *)sd_contentTypeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"image/jpeg";
case 0x89:
return @"image/png";
case 0x47:
return @"image/gif";
case 0x49:
case 0x4D:
return @"image/tiff";
case 0x52:
// R as RIFF for WEBP
if ([data length] < 12) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return @"image/webp";
}
return nil;
}
return nil;
}
这个文件是NSData的分类,只有一个方法,传入图片数据,根据图片的头标识来确定图片的类型。头标识都不一样,只需获取文件头字节,对比十六进制信息,判断即可。图片的格式存在 图片文件的前8个字节中. 第一个字节 0xFF 则图片为 JPEG , 为 0x89 则为 PNG。
13、IBOutlet 和 IBAction
IB的全称是 Interface Builder 界面构造器
IBOutlet 的属性是 weak :因为当我们将控件拖到Storyboard上,相当于新创建了一个对象,而这个对象是加到视图控制器的view上,view有一个subViews属性,这个属性是一个数组,里面是这个view的所有子view,而我们加的控件就位于这个数组中,那么说明,实际上我们的控件对象是属于view的,也就是说view对加到它上面的控件是强引用。当我们使用Outlet属性的时候,我们是在viewController里面使用,而这个Outlet属性是有view来进行强引用的,我们在viewController里面仅仅是对其使用,并没有必要拥有它,所以是weak的。**如果将weak改为strong,也是没有问题的,并不会造成强引用循环。**当viewController的指针指向其他对象或者为nil,这个viewController销毁,那么对控件就少了一个强引用指针。然后它的view也随之销毁,那么subViews也不存在了,那么控件就又少了一个强引用指针,如果没有其他强引用,那么这个控件也会随之销毁。
IBAction : 方法不能使用 return value。 相当于 void 。
14、UIView中的几个方法:setNeedsLayout、layoutIfNeeded、setNeedsDisplay、setNeedsDisplayInRect:
setNeedsLayout :Invalidates the layer’s layout and marks it as needing an update.作用在于标记需要更新布局,但是不会立即更新
layoutIfNeeded:Recalculate the receiver’s layout, if required. 如果,有需要刷新的标记,立即调用layoutSubviews进行布局
备注: setNeedsLayout 和 layoutIfNeeded 经常配合使用,调用layoutSubViews,即刻更新布局。
setNeedsDisplay 和 setNeedsDisplayInRect: You can use this method or the setNeedsDisplayInRect: to notify the system that your view’s contents need to be redrawn. This method makes a note of the request and returns immediately. The view is not actually redrawn until the next drawing cycle, at which point all invalidated views are updated. 即刻调用 drawRect: 渲染视图