ios进阶

OC总结篇 - OC基础

2019-10-16  本文已影响0人  亲爱的大倩倩

变量 - 属性关键字 - 初始化方法 - 分类与扩展 - 关联对象 - 代理和通知 - 多继承 - 异步 - 控制台

变量
<--成员变量,实例变量,属性变量的区别-->
成员变量 - interface括号中的全部都是成员变量(包含了实例变量,但实例变量也是成员变量的一种)
实例变量 - 是一种特殊的成员变量,用Class类实例化出来的对象就是实例变量,例如 UIButton *btn
属性变量 - 自动生成setter和getter,没有匹配成员变量的属性,会自动创建一个带下划线的成员变量
<--其他变量-->
在方法之外定义的
全局变量 - int var = 4
静态全局变量 - static int var = 4

在方法内部定义的
局部变量 - int var = 1
局部静态变量 - static int var = 1
属性关键字

@property (nonatomic,strong) NSMutableArray * dataArray;
1.读写权限 readwrite / readonly
2.原子性,也就是是否线程安全(atomic和nonatomic)
3.内存管理(引用计数方案)
默认修饰符
1.基本数据类型:atomic readwrite assign
2.普通OC对象: atomic readwrite strong

建议property最好是数据,不要是控件,控件建议写成私有的,在使用时我们进行数据传递,然后更新视图控件

readwrite: 生成getter和setter方法
readonly: 只生成getter方法
  atomic - 一个属性可能会被两个及以上线程同时访问,若使用这个属性,编译器会自动为变量加锁,保证线程安全
           可以保证对该成员变量的赋值和获取数据是线程安全的
           如果我们用atomic修饰了一个数组,那么对这个数组进行赋值和获取,是可以保证线程安全的
           但如果我们对数组进行操作,添加或移除对象,那么是不在atomic的负责范围内,所以是没有办法保证线程安全的
  nonatomic - 不加锁,不能保证线程安全,但是访问速度快
`引用计数`
引用计数有以下几个修饰
assign、weak、strong、 copy (ARC) 
assign、retain、release (MRC此处不再讨论)


如何使用
assign: 基础数据类型(NSInteger,float,double,CGFloat,BOOL)
weak:   代理
strong: 可变类型的属性(NSMutableArray,NSMutableDictionary,NSMutableString)
        控件
        对象
copy:    不可变类型的属性(NSArray,NSDictionary,NSString)
             block


下面分类讲解
*  assign / weak

*  assign - 修饰基本数据类型,如int, BOOL等
          - 修饰对象类型时,不改变其引用计数
          - 会产生悬垂指针,assign所修饰的对象,在被释放之后,assign指针仍指向原对象内存地址,如果这时通过assign指针,继续访问原对象,会导致内存泄露或程序异常

*  weak   - 不改变被修饰对象的引用计数,一般使用weak是用来解决循环引用问题
          - 所修饰的对象在被释放之后会自动置为nil

*  在修饰对象时,weak和assign都不改变被修饰对象的引用计数
*  weak可以修饰对象,assign既可以修饰对象,也可以修饰基本数据类型
*  weak所修饰的对象,在被废弃之后,weak指针置为nil. assign所修饰的对象,在被释放之后,assign指针仍指向原对象内存地址

------------------------------------------------------------

strong(ARC) / return(MRC) - 这两个关键字都是用来修饰对象

------------------------------------------------------------
copy

浅拷贝 = 指针拷贝,会增加被拷贝对象的引用计数,并没有发生新的内存分配,两个指针指向同一块原来的内存空间
深拷贝 = 内存拷贝,不会增加被拷贝对象的引用计数,因为没有新指针指向原来的内存空间,产生了新的内存分配,出现了两块内存

可变对象的copy和mutableCopy都是深拷贝
不可变对象的copy是浅拷贝,mutableCopy是深拷贝
copy方法返回的都是不可变对象,若被拷贝对象是可变对象,返回的也是不可变对象

问题✈️
NSMutableArray用copy修饰会出现什么问题?
程序异常,因为copy之后就变成不可变了,在进行增删改操作,会崩溃

通过上面我们知道了为什么可变对象不能用copy只能用strong,那么为什么不可变对象必须要用copy呢?
知道多态的,都知道父类指针可以指向子类对象,那么使用 copy 的目的是为了让本对象的属性不受外界影响
🌰
NSString的子类是NSMutableString
如果用strong声明一个string,然后将NSMutableString赋值给string,那么当NSMutableString的值变化的时候,NSString值也会变化,所以要用copy限制它的值不受外界影响

属性备注

ivar+set/get
如果不自己实现set和get方法,系统会帮我们合成
当我们声明一个属性的时候,系统在底层帮我们合成的set/get方法,在底层调用时有什么区别

初始化方法
1. alloc - 继承NSObject的对象的创建和分配内存
2. init - 是种工厂模式
          直接继承与NSObject的对象里面什么也没干,返回方法的调用者
          但是Foundation框架里面的NSArarry和NSDic的init做了很多事情
          之所以有init, 是特意让我们来重写的,也就是自己初始化
分类
"***作用***"
1.声明私有方法 - 把分类的头文件放到类的.m中,就满足了类既能使用这个方法,又对外不暴露
2.分解体积庞大的类文件
3.把Framework的私有方法公开

"***特点***"
1.在运行时进行决议 
  - 在编写完分类文件之后,并没有把分类中添加的内容附加到类上,而是在运行时通过runtime把分类内容真实的添加到类上
2.可以为系统类添加分类
<--分类结构体-->
可添加
1.实例方法
2.类方法
3.协议
4.实例属性(只声明了对应的get和set方法,但没有在分类中添加上相应的实例变量)
5.可以通过关联对象来为分类添加实例变量
<--源码分析-->
分析分类添加实例方法的逻辑如上图
1.倒叙遍历分类,最先访问最后编译的分类,获取该分类的方法列表,最后编译的分类方法列表最先被添加到分类方法数组中
2.然后将分类方法数组拼接到宿主类上
"重要总结❤️"
分类实现原理由运行时决议,不同分类中含有同名分类方法,谁最终生效,取决于谁最后参与编译
假如分类中添加的方法恰好是宿主类中的同名方法,分类方法会"覆盖"同名的宿主类方法
(消息传递过程中会优先查找数组靠前的元素,若找到了就会调用,但宿主类的同名方法仍然存在)

1. 分类添加的同名方法可以"覆盖"原宿主类方法
   -在我们消息发送过程中,是根据选择器名称来查找的,一旦查找到对应的实现就会返回
   -由于分类方法位于数组靠前的位置,若分类和宿主类方法重名,会先查找到分类的方法

2. 最后编译的分类同名方法才会最终生效,其他分类都会被覆盖掉
   -若我们添加两个分类,两个分类中都有同名方法,哪个会生效,取决于分类的编译顺序
   -因为是倒序编译,最后编译的分类同名方法才会最终生效,其他都会被覆盖掉

3. 名字相同的分类会引起编译报错
   -因为在生成具体分类的时候,经过runtime在编译过程中会把我们添加的分类名字以下划线方式拼接到宿主类上
   -如果名字相同,就会类似于我们定义了两个同名变量,会引起编译报错
扩展

只以声明的形式存在,没有具体实现,多数情况寄生于宿主类的.m中,也就是说,它不是独立存在实现的一个文件
在开发时,一般把扩展的声明放到宿主类的.m文件之中

"***作用***"
1.声明私有属性,是可以不对子类暴露的
2.声明私有方法,方便阅读
3.声明私有成员变量

"***特点***"
1.编译时决议
2.不能为系统类添加扩展
3.类的一个内部的私有声明
关联对象
"关联对象是什么"
AssociationsManager - AssociationsHashMap( key - Value )

所有类的关联对象都放在同一个全局容器中
关联对象是由系统提供的,由AssociationsManager类来管理
这个类有一个成员变量叫做AssociationsHashMap,我们创建的每一对象的关联对象,都存储在AssociationsHashMap里,它是个全局容器,,通过hash来实现的一个map
<--关联对象怎么使用-->
1.添加关联对象
void objc_setAssociatedObject(ocject对象,key,value,策略)
object是准备被关联的对象,key是我们要g关联的值得标识,value是要关联的值,policy是要通过哪种策略(copy,return或assign)将keyvalue关联到对象上

2.取关联对象的值
id objc_getAssociatedObject(object对象,key)

3.移除关联对象
void  objc_removeAssociatedObjects(object对象)
<--关联对象是如何实现的-->

代理和通知
@protocol
1.一对一
2.一般声明为weak避免循环引用
3.代理模式实现

NSNotification
1.一对多
2.是使用观察者模式来实现的,用于跨层传递消息的机制
3.观察者模式实现

代理是一种软件设计模式,也就是代理模式
在iOS中,以@protocol形式体现
传递方式是一对一

代理的工作流程

委托方把要求代理方实现的接口全都放在协议中
协议中可以声明属性或者方法
然后由代理方进行具体的实现

代理方和委托方是以什么样的关系实现的
通知
  1. 传递方式是一对多
  2. 是使用观察者模式来实现的,用于跨层传递消息的机制
    在业务开发过程中,涉及到数据层,网络层,业务逻辑层和UI层,一般的处理逻辑是需要由网络层传递给数据层,然后经过业务逻辑层加工,再去更新UI
    有些时候,可能需要网络层返回的数据,不经过业务逻辑层,直接到UI层,就会涉及到所谓的跨层传递
代理和通知的区别
  1. 代理是由代理模式实现的,通知是由观察者模式实现的
  2. 代理传递方式是一对一,通知传递方式是一对多
一对多
1.声明消息
[[NSNotificationCenter defaultCenter] addObserver:self
                                    selector:@selector(respondNotification:) name:@“FQNotification" object:nil];

2.响应消息
[[NSNotificationCenter defaultCenter] postNotificationName:@“FQNotification”object:@“llama"];

3.实现消息
- (void)respondNotification:(NSNotification*)aNotification
    {
       NSString *number= [aNotification object];
      NSLog(@"接收到的内容是%@",number);
    }

怎么实现通知机制

NS开头的文件没有源代码,所以不知道具体实现
假如让自己实现一个通知机制,会如何设计


在通知中心,也就是NSNotificationCenter这个系统类中,可能内部会维护一个map表,这里面的key就是NotificationName,value就是我们添加的observers,对同一个NotificationName可能添加多个observers,所以observers_List应该是一个数组列表.列表中的每个成员,都要包含通知接收的观察者,还要观察这个观察者调用的方法

KVO和通知的区别:
1.都是一对多
2.但是KVO只能监听属性(set变化),通知监听没有局限性
3.KVO是被观察者发出变化通知,观察者直接响应.但通知是被观察发出变化通知,由通知中心统一发出,然后观察者才响应,多了一步

多继承
ClassA实现了A方法,ClassB实现了B方法,ClassC即想实现A方法也想实现B方法,C++中用多继承就可以实现,但OC中没有
OC不支持多继承,但可以有多种方式模拟出多继承实现
1.Runtime的消息转发机制
2.协议protocol支持多继承
  ClassC遵循ClassA和ClassB的协议即可实现这两个类的协议方法
异步
1. 多线程,异步加载数据
    让子线程去处理耗时操作的代码块,再回到主线程刷新
    ```
        __weak typeof(self) weakSelf = self;

        dispatch_async(dispatch_get_global_queue(0, 0), ^{

            // 处理耗时操作的代码块...

            UIImageView *imgView = weakSelf.imgViewArray[i];

            UIImage *img = [UIImage imageNamed:dataArray[i]];

            //通知主线程刷新

            dispatch_async(dispatch_get_main_queue(), ^{

                imgView.image = img;

            });

        });
    ```
2. YYTransaction,异步绘制 (不会用)
3. 用Runloop来实现异步
    监听Runloop的空闲状态,在Runloop即将休眠时(空闲时)再去绘制图片
控制台
po
bt(打断点,然后bt,会出现调用顺序)
上一篇下一篇

猜你喜欢

热点阅读