iOS基础 - wustZxl/Note-ios GitHub Wiki

成员变量和属性

iOS成员变量和成员属性的区别

声明方式

属性@property会自动创建一个以下划线开通的实例变量,并且会自动生成setter/getter函数;

声明位置

看看位置 都可以在.h和.m文件中声明,访问权限会发生改变

  • 在.h中声明的属性,可以被子类或外部访问,在.m中声明的属性,外部(含子类)不可访问。
  • 在.m中声明的成员变量,外部(含子类)不可访问,在.h中声明的成员变量,访问权限如下:
objc_AssociationPolicy modifier
@public 都可访问
@protected 只有子类可以访问,外部不可访问
@private 外部(含子类)不可访问
@packeage 同一个package下都可访问
作用域和访问方式

属性用.属性名的方式获取,成员变量用->变量名的方式获取

其他

Category可以添加属性,不能添加成员变量,但是添加的属性不会自动生成getter/setter函数

@property 属性

iOS@property属性相关总结
@property = ivar(实例变量) + getter + setter

常用的特性关键字:
  • 原子性(多线程管理):nonatomic / atomic
  • 存取方法: readonly / readwrite
  • 内存管理: retain / copy / assign
  • 强弱引用: strong / weak

self和下滑线区别

self方法实际上是用了get和set方法间接调用,下划线是直接对变量操作。

  • 使用self会使引用计数+1, _XXX不会使引用计数+1
  • 使用self比较好,因为这样可以兼容懒加载,同时避免了使用下划线的时候忽略了self这个指针
  • 在block中,下划线的方式容易造成循环引用,如果这个block是全局的(成员变量或者属性),那么在里面使用下划线会形成循环引用。
    • block的机制相当于一个VC,在block里面相当于是block调用了该VC,而VC又调用了block, 故会形成循环引用。
    • 且block中,下划线的方式会出现无法释放当前对象的情况,
    • 尽量用弱引用的方式,weakself.XXX 可以防止block里对self的循环引用

block嵌套引起的循环引用

基础

  • weakself:不增加self的引用计数,并且还可以使用它,所以它能防止循环引用(该释放就释放)
  • strongself:在定义的区域内保证指向的内容不被释放,所以他用在确保代码执行的时候。(防止self变成nil,延迟self的生命)
  • “引用循环”是指双向的强引用,那些单向的强引用

UIScrollView代理:

  • scrollViewDidEndDragging 手指停止拖动到时候开始执行
  • scrollViewDidEndDecelerating 手指离开屏幕后scrollview还会继续滚动一段时间直到停止后才会执行。

tableView

几种刷新机制
  • self.tableview reloadData]; 刷新整个UITableView
  • 局部刷新:
    • 注册notification的时候要注意是否需要在多个地方注册;
tableViewCell左滑事件
- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath;

cell尾部滑动事件

  • 在action里面[self.tableView setEditing:NO animated:YES]; 即左滑删除时退出编辑模式
  • 返回的action数组 actionsConfiguration.performsFirstActionWithFullSwipe = NO; 使其不支持滑动到顶部自动删除改行。

类型使用

  • NSMutableAttributedString 给String添加 font, color等属性
  • 与NSRange结合使用可以让一个string有不同的属性
    • NSRange r1 = NSRangeFromString(str);
    • NSRange r2 = [str rangeOfString:str];

NS_OPTIONS 与 NS_ENUM

参考详解枚举NS_OPTIONS与NS_ENUM的区别与格式

  • 枚举命名尽量用系统的风格,比如枚举名为UIViewAutoresizing,具体的值为UIViewAutoresizingNone。简单来讲就是(枚举名+状态)
  • 如果要实现一个变量保存多个枚举值,则用NS_OPTIONS
  • 用NS_ENUM和NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。
  • 在处理枚举类型的switch语句中不要实现default分支,这样的话,加入新的枚举值后,编译器就会提示开发者:switch语句并未处理所有枚举,便于避免漏掉加入的枚举逻辑
  • 预留一个枚举值,用于扩展或者用于表示没有的情况。

push和present的区别

  • 基本区别

    • pushVC是导航控制器入栈的方式切换页面, 与popVC对应。
    • presentVC是模态切换的方式切换页面,与dismissVC对应。
  • 对生命周期的影响

    • present
     [self presentModalViewController:controller animated:YES];
      	// self 不会调用dealloc自我销毁。
      	// self.set 只会再第一次出现时调用ViewDidLoad加载,之后再执行该函数时只会调用:(void)viewWillAppear:(BOOL)animated;
     [self dismissModalViewControllerAnimated:YES];
      	// self 会调用dealloc
      	// self之下的一个Controller会调用(void)viewWillAppear:(BOOL)animated;
    
    • push
    [self.navigationController pushViewController:controller animated:YES];
    // 上层的ViewController会调用viewdidload.	 
    [self.navigationController popViewControllerAnimated:YES];
    // 上层的ViewController会调用dealloc
    
  • A->B->C, 返回

    • present方法:present只能逐级返回,如果要返回到隔层的VC, 可以通过notification来通知返回。
    • push返回方法pop:
      • 直接返回上一层: [self.navigationController popViewControllerAnimated:YES];
      • 返回到某一层(指定):[self.navigationController popToViewController:vcHome animated:YES];
      • 返回到根控制器: [self.navigationController popToRootViewControllerAnimated:YES]; 

Category 分类

Category VS Extension 原理详解

  • category只能给某个已有的类扩充方法,不能扩充成员变量。
  • category中也可以添加属性,只不过@property只会生成setter和getter的声明,不会生成setter和getter的实现以及成员变量。
  • 如果category中的方法和类中原有方法同名,运行时会优先调用category中的方法。也就是,category中的方法会覆盖掉类中原有的方法。所以开发中尽量保证不要让分类中的方法和原有类中的方法名相同。避免出现这种情况的解决方案是给分类的方法名统一添加前缀。比如category_。
  • 如果多个category中存在同名的方法,运行时到底调用哪个方法由编译器决定,最后一个参与编译的方法会被调用。
  • 调用优先级: Category -> 本类 ->

Extension 扩展

Category与Extension

  • extension在编译期决议,它就是类的一部分,但是category则完全不一样,它是在运行期决议的。extension在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它、extension伴随类的产生而产生,亦随之一起消亡。
  • extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension,除非创建子类再添加extension。而category不需要有类的源码,我们可以给系统提供的类添加category。
  • extension可以添加实例变量,而category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。
  • extension和category都可以添加属性,但是category的属性不能生成成员变量和getter、setter方法的实现。

Associated Objects 关联对象

Associated Objects 的实现原理 给我的感觉有点类似Category,对类的属性进行扩展,弥补了Category只能对方法进行扩展的缺陷。

// 给对象添加关联对象,传入nil可以移除已有的关联对象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
// 用于获取关联对象
id objc_getAssociatedObject(id object, const void *key);
// 用于移除一个对象的所有关联对象
void objc_removeAssociatedObjects(id object);

objc_AssociationPolicy对应的属性修饰符:

objc_AssociationPolicy modifier
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic, strong
OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic, copy
OBJC_ASSOCIATION_RETAIN atomic, strong
OBJC_ASSOCIATION_COPY atomic, copy

Category 与 Associated Objects 结合使用

Delegate

关于delegate 与 protocol 的理解 所以,见过以下两种运用场景,后面看到了再添加:

  • 有A, B, C 三个对象,A是一个ADelegate, B有一个ADelegate的属性(或者成员变量), C有一个B的属性(或者成员变量), 故C中必须实现ADelegate, 需设置代理 delegateTest.delegate = self;, 由B来调用delegate的方法, C来实现delegate的方法。
  • 有A, B, C 三个对象,A是一个delegate, ADelegate, B有一个ADelegate属性(或者成员变量)和一个C的属性(或变量), 在B中实例化C,并将C赋值给B的delegate属性 self.delegate = self.C , 在C中实现delegate, 由B调用(或者传值给C)

总结:所以delegate的使用,需要有一个类来调用delegate方法,另外一个类实现delegate方法,通过delegate完成两个类之间的交互。