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被调用以下几种方式:

1.init的时候并不会触发layoutSubviews,但是在initWithFrame初始化的时候肯定会调用layoutSubviews。
2.addSubView 肯定会调用 layoutSubviews。
3.当view的frame有变化的时候,就会调用layoutSubviews。
4.滚动UIScrollView时候会触发layoutSubviews。
5.旋转屏幕的时候,会触发layoutSubviews。
2:layoutSubviews方法:这个方法,默认没有做任何事情,需要子类进行重写
setNeedsLayout方法: 标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用
layoutIfNeeded方法:如果,有需要刷新的标记,
立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)
如果需要立即刷新的就调用[view setNeedsLayout];标记为需要布局后,然后就会调用[view layoutIfNeeded];
如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局
在视图第一次显示之前,标记肯定是“需要刷新”的,所以直接调用[view layoutIfNeeded]就会进行立即更新
3:drawRect:(CGRect)rect方法:重写此方法,执行重绘任务
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的语法糖
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字节空间即可。

结论: 占用空间对象,直接使用指针内部空间节约内存开销

posted on 2020-12-28 12:53  风zk  阅读(203)  评论(0编辑  收藏  举报

导航