055*: (load、initialize)(UIView和CALayer)(@synthesize 和 @dynamic)(layoutIfNeeded和setNeedsLayout)(include import @class)(id和NSObject ,instancetype)(NSNumber)
(load、initialize)
(UIView和CALayer)、
(@synthesize 和 @dynamic)
(layoutIfNeeded和setNeedsLayout)
(include import @class)
(id和NSObject ,instancetype)
一:load、initialize,加载时机,调用顺序,主动调用
1:Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗? 加载时间
- 有load方法
- load方法在runtime加载类、分类的时候调用
- load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用。
2:initialize方法如何调用,以及调用时机
- 当类第一次收到消息的时候会调用类的initialize方法
- 是通过 runtime 的消息机制 objc_msgSend(obj,@selector()) 进行调用的
- 优先调用分类的 initialize, 如果没有分类会调用 子类的,如果子类未实现则调用 父类的
3:load、initialize方法的区别什么?它们在category中的调用的顺序?以及出现继承时他们之间的调用过程?
- load 是类加载到内存时候调用, 优先父类->子类->分类
- initialize 是类第一次收到消息时候调用,优先分类->子类->父类,会覆盖子类和父类的方法。只执行一次。
- 同级别和编译顺序有关系
- load 方法是在 main 函数之前调用的
二:UIView和UILayer区别
1:继承
@interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, UIFocusItemContainer, CALayerDelegate> @property(nonatomic,readonly,strong) CALayer *layer; @end @interface UIResponder : NSObject <UIResponderStandardEditActions> @interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; - (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
2:作用
UIView:主要是用来构建用户界面的,布局界面、可以响应事件、管理子视图。
CALayer:用来绘制内容的,对内容进行动画处理,在UIView上进行显示。
3:关系
UIView 持有一个 CALayer 的属性,并且是该属性的代理,用来提供一些 CALayer 行的数据,例如动画和绘制。
//绘制相关 - (void)displayLayer:(CALayer *)layer; - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx; - (void)drawRect:(CGRect)rect //动画相关 - (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;
CALayer属于QuartzCore.famework中,是用来绘制内容的,对内容进行动画处理。
数据UIKit.framework框架,负责渲染矩形区域的内容,为矩形区域添加内容,响应区域的触摸事件,布局和管理一个或多个子视图。
UIView是由CoreAnimation来实现的。它真正的绘图部分,是由一个叫CALayer(Core Animation Layer)的类来管理。
4:三层树
UIView 的 layer 树形在系统内部分别是:
1.逻辑树,这里是代码可以操纵的;
2.动画树,是一个中间层,系统就在这一层上更改属性,进行各种渲染操作;
3.显示树,其内容就是当前正被显示在屏幕上得内容。
三:@synthesize 和 @dynamic 分别有什么作用?
1:@property有两个对应的词,@synthsize @dynamic如果都没写,那么默认就是@synthsize var = _var;
2:@synthsize 如果没有手动实现setter getter方法那么自动生成 ,自动生成_var变量
3:@dynamic告诉编译器:属性的setter,getter方法有用户自己实现,不自动生成.
假如一个属性被声明为@dynamic var ; 那么不实现setter getter方法,编译阶段不会报错,但是使用instance.var = someVar ,会crash,没有实现set方法.
四:setNeedsLayout和layoutIfNeeded的区别,layoutSubviews
布局、绘制、约束
1:layoutSubviews被调用以下几种方式:
setNeedsLayout方法: 标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用
layoutIfNeeded方法:如果,有需要刷新的标记,
如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局
在视图第一次显示之前,标记肯定是“需要刷新”的,所以直接调用[view layoutIfNeeded]就会进行立即更新
setNeedsDisplay方法:标记为需要重绘,异步调用drawRect
setNeedsDisplayInRect:(CGRect)invalidRect方法:标记为需要局部重绘
4:基于约束的AutoLayer的方法:updateConstraints
1、setNeedsUpdateConstraints
当一个自定义view的某个属性发生改变,并且可能影响到constraint时,需要调用此方法去标记constraints需要在未来的某个点更新,系统然后调用updateConstraints
.
2、needsUpdateConstraints
constraint-based layout system使用此返回值去决定是否需要调用updateConstraints
作为正常布局过程的一部分。
3、updateConstraintsIfNeeded
立即触发约束更新,自动更新布局。
4、updateConstraints方法
自定义view应该重写此方法在其中建立constraints.
注意:要在实现在最后调用[super updateConstraints]
当一个view更新其约束时,先触发[view setNeedsUpdateConstraints]; 然后执行updateConstraints方法,在这个里面重写view的目标约束(注意:在这个方法最后调用[super updateConstraints])。 如果需要动画效果的时候, 在执行完1:[view setNeedsUpdateConstraints] 方法后, 2:执行[view updateConstraintsIfNeeded]方法,
3: 接着写view的动画效果,[UIView animateWithDuration:2 animations:^{ [self.view layoutIfNeeded] }]; 对于updateConstraintsIfNeeded这个方法并不是必须的,但是有时候不调用就无法起到我们的效果。但是,官方都是这么写的,从约束的更新原理上讲,这应该写上
五:#include #import @class
1:使用#include引入同一个类2次的时候编译器会报错:因为#include相当于拷贝头文件中的声明内容,会报重复定义的错误(Duplicate interface definition for class ‘xx’);但是#import解决了重复导入报错的问题!2:#import是object-c导入头文件的关键字,#include是C/c++导入头文件的关键字,使用#import导入头文件会自动只导入一次,不会重复导入。
3:@class是告诉编译器某个类的声明,当执行时,才会去查看类的实现文件,可以解决头文件的相互包含。
4:#import <>用来包含系统的头文件,#import ""是包含用户的头文件。
5:#import会链入该头文件的全部信息,包括该类的属性、方法等;而@class只是告诉编译器,其后面声明的名称是类的名称,不会引入该类的属性等等信息。
如果有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,如果使用#import来相互包含,那么就会出现编译错误,如果使用@class在两个类的头文件中相互声明,则不会 有编译错误出现。所以,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。 在实现这个接口的实现类中,如果需要引用这个类的实体变量或者方法之类的,还是需要import在@class中声明的类进来.
六:id、instancetype、NSObject ,
1:id是通用类型的指针,编译时不进行类型检查
2:id和instancetype的相同点、不同点
1)相同点
都可以作为方法的返回类型
2)不同点
instancetype只能作为返回值;id可以做为返回值,也可以作为参数的类型
instancetype可以返回和方法所在类相同类型的对象;id只能返回未知类型的对象
3:NSObject和id
NSObject对象会进行编译时检查(需要强制类型转换)
id可以直接用,不需要强制类型转换
id是动态类型,编译器不再检查类型
3.1、 id 与 NSObject *
(1) id 是 Objective-C 对象,但是并不一定是NSObject对象,并非所有的Foundation/Cocoa对象都是继承于NSObject对象的,比如NSProxy。同时,id与NSObject对象之间有很多的共同方法,比如retain与release等方法。更一步来说:所有的对象本质来说都是 id 类型的。
(2) 对于id来说,你可以调用任意可见的selector,编译器和IDE,不会进行类型检查,这个时候就需要你自己进行类型检查并且进行类型转换,来确保这些调用不会出错。而对于NSObject *类型,只能调用NSObject对象所声明的selector,不能调用它子类的selector,编译器会进行检查。
(3) 对于一些不想或者不能进行类型检查的地方,可以使用id。比如在集合(array, collection)类型中,比如在一些你并不知道方法的返回类型的地方(比如alloc),比如我们经常声明delegate为id类型,在运行的时候再使用respondToSelector:来进行检查。
3.2、id
使用id来声明一个对象,相当于告诉编译我们并不知道这个对象的类型,但是它实现NSObject protocol。一个这种类型的指针,即可以用来指向NSObject对象,也可以用来指向NSProxy对象,因为NSObject对象与NSProxy对象都是现了NSObject protocol。
七:NSNumber:小数据类型、语法糖,简写
NSNumber 是 NSValue的子类
使用NSNumber对象来创建和初始化不同类型的数字对象
NSArray、NSDictionary中只能存放OC对象, 不能存放int、float、double等基本数据类型,先将基本数据类型包装成OC对象 才能存储。
结构体数据类型用NSValue存储,NSNumber无法存储。
OC 常用的结构体类型:
NSRect(表示一个位置和尺寸)
NSPoint(表示坐标位置)、
NSSize(表示尺寸)、
NSRange(表示范围)
//NSNumber的语法糖 NSNumber *intNumber = [NSNumber numberWithInt:100]; NSNumber *intNumber2 = @100; //不可变字符串的语法糖 NSString *string = @ "hanjunqiang" ; //可变字符串的语法糖 NSMutableString *mString = @ "大爱中华" .mutableCopy; //后缀不能丢 //不可变数组的语法糖 NSArray *array = @[@ "1" ,@ "2" ,@ "3" ,@ "4" ]; NSLog(@ "%@" ,array); //访问数组元素的语法糖 NSLog(@ "%@" ,array[1]); //可变数组的语法糖 NSMutableArray *mArray = @[@ "1" ,@ "2" ,@ "3" ,@ "4" ].mutableCopy; //字典的语法糖 //字典对象[key值]取出对应的value值 NSDictionary *dict = @{@ "a" :@ "1" ,@ "b" :@ "2" }; //key值在冒号前,value值在冒号后 NSLog(@ "%@" ,dict); NSLog(@ "%@" ,dict[@ "a" ]); //可变字典可以赋值和修改值 NSMutableDictionary *mDic = @{@ "a" :@ "1" ,@ "b" :@ "2" }.mutableCopy; mDic[@ "a" ]=@ "100" ; NSLog(@ "%@" ,mDic[@ "a" ]);
TaggedPointer
是标记指针
。标记的是小对象
,可以记录小内容的NSString、NSDate、NSNumber
等内容。是内存管理
(节省内存开销
)的一种有效手段。
例如:使用
NSNumber
记录10
【常规操作】
去堆
中开辟
一个内存
,用于存储
这个对象
,在对象
内部记录
这个内容10
,然后需要
一个指针
,指向堆
中的内存首地址
。
(内存开销
:指针8字节
+堆
中对象
的空间大小
)【
TaggedPointer
小对象】
记录一个10
,根本不用
去堆
中开辟内存
,直接利用指针
拥有的8字节
空间即可。结论:
占用空间
较小
的对象
,直接使用指针内部空间
,节约内存开销