声明私有方法
分解体积庞大的类文件
把Framework的私有方法公开
运行时决议
可以为系统类添加分类
实例方法
类方法
协议
属性(只生成对应get和set方法并未添加实例变量)
运行时决议
分类添加的方法可以覆盖(效果是覆盖实际宿主类的同名方法仍然存在)原类方法 消息传递的过程中优先查找靠前的元素。
同名分类方法谁能生效取决于编译顺序
名字相同的分类会引起编译报错
一个类(子类)继承于另一个类(父类),那么子类不仅拥有父类所有的属性和方法,而且可以创建属于自己的属性和方法。
类别和继承的使用,以下两种情况只能使用继承,类别无法实现。第一种,新扩展的方法与原方法同名,但是还需要使用父类的实现。因为类别会覆盖原类的实现,无法访问到原来的方法。第二种,扩展类的属性,这个类别无法做到。以下两种情况最好使用类别,第一种,针对系统提供的一些类,例如:NSString,NSArray,NSNumber等类,系统本身不提倡使用继承去扩展方法,因为这些类内部实现对继承有所限制,所以最后使用类别来进行方法扩展。第二种,类别支持开发人员针对自己构建的类,把相关的方法分组到多个单独的文件中,对于大型而复杂的类,这有助于提高可维护性,并简化单个源文件的管理。
可以添加,但是不能在分类的声明和定义的时候直接为分类添加成员变量,可以通过关联对象的技术添加成员变量,来达到分类可以添加成员变量的效果。
关联对象由AssociationsManager管理并在AssociationsHashMap存储。
所有对象的关联内容都在同一个全局容器中
setObjectValue 传nil 就可以清除
声明私有属性
声明私有方法
声明私有成员变量
编译时决议
只以声明的形式存在,多数情况下寄宿于宿主类的.m中。
不能为系统类添加扩展。
一种软件设计模式,代理模式
iOS中以@protocal形式体现
传递方式一对一。
一般声明为weak以规避循环引用。
使用观察者模式来实现的用于跨层传递消息的机制。
传递方式为一对多。
在通知中心 NSNotificationCenter系统类中内部维护一个Map表或者是一个字典,他的key是NotificationName就是我们addobserver的时候要传递的一个监听名称作为字典的key,它的value就是我们添加的observer,对于同一个NotificationName可能添加多个observer,所以对应的observer应该是一个组列表,列表中的每一个成员应该包含通知接收的一个观察者还要包含关于这个观察者调用的方法, 比如说:我们收到这个通知后,观察者回调方法是哪个,那么在这个列表当中元素中也会体现关于通知回调方法的相关数据信息。
kvo是观察者设模式的一种实现
系统采用了isa(isa-swizzling)混写技术来实现kvo
当我们注册了一个对象的观察者的时候实际上是调用了addobserver:forkeypath:后系统会为我们在运行时动态的创建一个NSKVONotifying_A类,又会把原来类的将A的isa指针指向新创建的NSKVONotifying_A,把isa指针的指向修改实际上就是isa混写技术。NSKVONotifying_A是原来A类的子类,是为了重写原来类的setter的方法,然后子类通过对setter方法的重写来达到可以通知所有观察者的目的。
可以生效 因为 setValue:forKey:最终会调用setter方法
不生效 因为无法触发系统的kvo,系统为我们提供的kvo相当于在setter方法中插入了willChangeValueForKey和didChangeValueForKey,所以我们也可以手动触发kvo方式来触发kvo生效
如下图:
使用setter方法改变值kvo才会生效。
使用setValue:forKey:改变值kvo才会生效。
成员变量直接修改需手动添加kvo才会生效。
键值编码技术
-(id)valueForKey:(NSString *)key
-(void)setValue:(id)value forKey:(NSString *)key
(1)首先会按照顺序依次查找getKey:、key、isKey、_key:这四个方法,只要找到这四个方法当中的任何一个就直接调用该方法;
(2)如果没有找到,那么这个时候会查看accessInstanceVariablesDirectly方法的返回值,如果返回的是NO(也就是不允许直接访问成员变量),那么会调用valueforUndefineKey:方法,并抛出异常“NSUnknownKeyException”;
(3)如果accessInstanceVariablesDirectly方法返回的是YES,也就是说可以访问其成员变量,那么就会按照顺序依次查找 _key、_isKey、key、isKey这四个成员变量,如果找到了,就直接取值;如果依然没有找到成员变量,那么会调用valueforUndefineKey方法,并抛出异常“NSUnknownKeyException”。
(1)首先会按照顺序依次查找setKey:方法和_setKey:方法,只要找到这两个方法当中的任何一个就直接传递参数,调用方法;
(2)如果没有找到setKey:和_setKey:方法,那么这个时候会查看accessInstanceVariablesDirectly方法的返回值,如果返回的是NO(也就是不允许直接访问成员变量),那么会调用setValue:forUndefineKey:方法,并抛出异常“NSUnknownKeyException”;
(3)如果accessInstanceVariablesDirectly方法返回的是YES,也就是说可以访问其成员变量,那么就会按照顺序依次查找 _key、_isKey、key、isKey这四个成员变量,如果查找到了,就直接赋值;如果依然没有查到,那么会调用setValue:forUndefineKey:方法,并抛出异常“NSUnknownKeyException”。
读写权限
原子性
引用计数
读写权限
readonly
readwrite
原子性
atomic (线程安全)举例:对数组添加和移除是无法保证安全的,只能保障赋值和获取
nonatomic
atomic是一种自旋锁。当我们一旦获取了自旋锁,线程会一直保持该锁直至显示释放该锁。而在单一线程的操作中,其实并不需要去获取这个锁来防止多线程操作对于成员变量的同时修改,所以选择nonatomic可以避免多余的性能损耗
引用计数
retain/strong
assign/unsafe_unretained
weak
copy
assign:修饰基本数据类型,如int,BOOL等,修饰对象类型事,不改变其引用计数,修饰的对象在被释放后指针仍然指向原对象的内存地址,所以会产生垂悬指针
weak:不改变被修饰对象的引用计数,所修饰对象在被释放之后会被自动置为nil。
copy
浅拷贝
深拷贝
是否开辟了新的内存空间
是否影响了引用计数
copy关键字
可变对象copy和mutableCopy都是深拷贝。
不可变对象copy都是浅拷贝,mutableCopy是深拷贝。
copy方法返回的都是不可变对象。
会导致不可预计的程序异常问题,对array增删等操作会造成crash等行为。
如果复制过来的是NSMutableArray,copy之后是NSArray
如果赋值过来的是NSArray,copy之后是NSArray
@property (nonatomic,retain)id obj;
- (void)setObj:(id)obj{
if (_obj != obj) {
[_obj release];
_obj = [obj retain];
}
}
以main为分界,load方法在main函数之前执行,initialize在main函数之后执行
只要程序启动就会将所有类的代码加载到内存中, 放到代码区(无论该类有没有被使用到都会被调用)
// load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次
// 如果存在继承关系, 会先调用父类的load方法, 再调用子类的load方法
当当前类第一次被使用的时候就会调用(创建类对象的时候)
// initialize方法在整个程序的运行过程中只会被调用一次, 无论你使用多少次这个类都只会调用一次
// initialize用于对某一个类进行一次性的初始化
// initialize和load一样, 如果存在继承关系, 会先调用父类的initialize再调用子类的initialize
#pragma mark 在应用程序加载完毕之后调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"应用程序启动");
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(@"即将从前台进入后台");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(@"已经从前台进入后台");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(@"即将从后台进入前台");
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(@"已经从后台进入前台");
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(@"应用程序被杀死或关闭");
}
- (instancetype)init{
if (self = [super init]) {
NSLog(@"1.init初始化");
}
return self;
}
//当时xib加载时
- (void)awakeFromNib{
[super awakeFromNib];
NSLog(@"2.Nib加载成功");
}
- (void)loadView{
[super loadView];
NSLog(@"3.加载view。");
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"4.载入完成,可以进行自定义数据以及动态创建其他控件");
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSLog(@"5.视图将出现在屏幕之前");
}
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
NSLog(@"6.将要对子视图进行调整");
}
- (void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
NSLog(@"7.对子视图进行调整完毕");
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSLog(@"8.视图已在屏幕上渲染完成");
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
NSLog(@"9.视图将被从屏幕上移除");
}
- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
NSLog(@"10.视图已经被从屏幕上移除");
}
- (void)dealloc{
NSLog(@"11.视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放");
}
- (void)didReceiveMemoryWarning{
[super didReceiveMemoryWarning];
NSLog(@"12.内存警告");
}
- (void)didAddSubview:(UIView *)subview{
[super didAddSubview:subview];
NSLog(@"1.当视图添加子视图时调用");
}
- (void)willRemoveSubview:(UIView *)subview{
[super willRemoveSubview:subview];
NSLog(@"2.当子视图从本视图移除时调用");
}
- (void)willMoveToSuperview:(nullable UIView *)newSuperview{
[super willMoveToSuperview:newSuperview];
NSLog(@"3.当视图即将加入父视图时 / 当视图即将从父视图移除时调用");
}
- (void)didMoveToSuperview{
[super didMoveToSuperview];
NSLog(@"4.当视图加入父视图时 / 当视图从父视图移除时调用");
}
- (void)willMoveToWindow:(nullable UIWindow *)newWindow{
[super willMoveToWindow:newWindow];
NSLog(@"5.当视图即将加入window视图时 / 当视图即将从window视图移除时调用");
}
- (void)didMoveToWindow{
[super didMoveToWindow];
NSLog(@"6.当视图加入window视图时 / 当视图从window视图移除时调用");
}