本文介绍: Effective Objective-C 52个有效方法一书的个人笔记

1. 了解Objective-C语言的起源

2. 在类的头文件中尽量少引入其他头文件

3. 多用字面语法,少用与之等价的方法

例子

/// 创建
NSNumber *numObj = @(1)
NSArray *arr = @[@"cat", @"dog"]
NSString *str = @"sdfsd"
NSDictionary *dic = @{@"first": @"tom"}
/// 取下标
NSString *dog = arr[1]
NSString *name = dic[@"first"]

4. 多用类型常量,少用#define预处理指令

// header file
extern NSString *const 变量名

// implementation file
NSString *const 变量名 = @"";

5. 用枚举表示状态选项状态

6. 理解属性这一概念

- (id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
  if((self = [super init])) {
    _firstName = [firstName copy];
    _lastName = [lastName copy];
    /// 这里不能使用属性所对应的设置方法 原因见第七条
  }
  return self;
}

7. 在对象内部尽量直接访问实例变量

8. 理解“对象等同性”这一概念

NSMutableSet *set = [NSMutableSet new];
NSMuatableArray *mutaArrA = [@[@(1), @(2)] mutableCopy];
NSMuatableArray *mutaArrB = [@[@(1), @(2)] mutableCopy];
NSMuatableArray *mutaArrC = [@[@(1) mutableCopy];
[set addObject: mutaArrA];
[set addObject: mutaArrB];  // 此时set中只会有一个元素 因为AB两个数组在等通行判断上返回true
[set addObject: mutaArrC];  // 此时set中含有两个元素
[mutaArrC addObject: @(2)]; // 现在set中含有两个相同的两个元素
NSSet *setB = [NSMutableSet copy];  // setB又只会含有一个元素

9.以“类族模式隐藏实现细节

10. 在既有类中使用关联对象存放自定义数据

11. 理解objc_msgSend的作用

12. 理解消息转发机制

[外链图片转存失败,源站可能有防盗链机制,建议图片保存下来直接上传(img-O8DMWib4-1662015588609)(https://gitee.com/Lcmzy89/drawing-bed/raw/master/image/20220629161133.png消息转发”)]

13. 用“方法调配技术调试“黑盒方法”

14. 理解“类对象”的用意

[外链图片转存失败,源站可能有防盗链机制,建议图片保存下来直接上传(img-X3HLwuQx-1662015588609)(https://gitee.com/Lcmzy89/drawing-bed/raw/master/image/111.png “objc_class的结构体”)]

  • 在继承体系查询类型信息
    1. isMemberOfCalss,判断对象是否是某个特定类的实例。isKindOfClass判断随想是否是某个类或其派生类的实例。
    2. 也可以使用 == 判断类对象是否等同的方式(不建议)。因为类对象都是单例,在应用程序范围内,每个类的Class仅有一个实例。即便能这样做,我们应该尽量使用类型信息查询方法,而不应该直接比较两个类对象是否等同,因为前者可以正确处理那些使用了消息传递机制(参见第12条)的对象。比方说,某个对象可能会把其收到的所有选择子都转发给另外一个对象。这样的对象叫做“代理”(proxy),此种对象均以NSProxy为根类。通常情况下,如果在此种代理对象上调用class方法,那么返回的是代理对象本身(此类是NSProxy的子类),而非接受的代理的对象所属的类。然而,若是改用“isKindOfClass:”这样的类型信息查询方法,那么代理对象就会把这条消息转给“接受代理的对象”(proxiedobject)。也就是说,这条消息的返回值与直接在接受代理的对象上面查询其类型所得的结果相同。因此,这样查出来的类对象与通过class方法所返回的那个类对象不同,class方法所返回的类表示发起代理的对象,而非接受代理的对象。
  • isa指针所指的对象是另外一个类,叫做元类,用来表述类对象本身所具备的元数据。“类方法”就定义于此处,可以理解成类对象的实例方法。每个类仅有一个“类对象”,而每个类对象,仅有一个与之相关的“元类”。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vXKDIkbY-1662015588610)(https://gitee.com/Lcmzy89/drawing-bed/raw/master/image/sjdhfvdjshfvjdshvfhjds.png “某个实例的继承体系”)]
  • isKindOfClassobj.class == SomeClass.class区别
    如果某个对象将其所有的选择子都转发给另外一个对象,isKindofClass会将被代理的对象返回。class方法只会将自己的类型值返回,推荐用isKindOfClass.

15. 用前缀避免命名空间冲突

16. 提供全能初始化方法

17. 实现description方法

18. 尽量使用不可变对象

19. 使用清晰而协调的命名方式

  • 如果方法的返回值是新创建的,那么方法名的首个词应是返回值都得类型,比如intValue,属性的存取方法除外
  • 应该把表示参数类型的名词放在参数前面
  • 如果方法要在当前对象上执行操作,那么就应该包含动词,若执行操作时还需要参数,则应该把在动词后面加上一个或多个名词
  • 不要使用str这种简称,应该使用string这样的全称
  • Boolean属性应加is前缀,如果某个方法返回非属性的Boolean值,那么应该根据其功能,选用has或is当前
  • get这个前缀留给那些借由“输出函数”来保存返回值的方法

20. 为私有方法名加前缀

21. 理解Objective-C错误模型

22.理解NSCopying协议

  • 拷贝对象通过copy方法完成,需要实现NSCopying协议。真正需要实现的是copyWithZone,Copy方法由NSobject实现,内容是以默认区为参数调用copyWithZone。
    例:
    -(id)copywithZone:(NSZone*)zone {
      EOCPerson *copy = [[[self class] allocWithZone:zone] initwithFirstName:firstName andLastName:lastName];
      return copy;
    }
    
    • 有的时候,除了拷贝对象,还需要完成一些其他操作。比如EOCPerson中含有一个数组,这时候需要把数组拷贝过来。
    • NSMutableCopying协议,需要实现 - (id)mutableCopyWithZone:(NSZone *)zone方法
    • Copy返回的一定是不可变版本。即使原版本可变的。MutableCopy同理。
    • 系统框架一般是默认浅拷贝的。

23.通过委托数据源协议进行对象间通信

24.将类的实现方法分散到便于管理各个分类之中

25.总是为第三方类的分类名称加前缀

26.勿在分类中声明属性

  • 除了class-continuation分类,其他分类都无法向类中新增实例变量。

27.使用“class-continuation分类”隐藏实现细节

28.通过协议提供匿名对象

29.理解引用计数

30.以ARC简化引用计数

31.在delloc方法中只释放引用并解除监听

32.编写异常安全代码”时留意内存管理问题

33.以弱引用避免保留环

34.以“自动释放池”降低内存峰值

35.用“僵尸对象”调试内存管理问题

36.不要使用retainCount

  • retainCount 用来查询对象当前的保留计数。ARC下已经废弃。

37.理解“块”这一概念

38.为常用块类型创建typedef

39.用handler块降低代码分散程度

40.用块引用其所属对象时不要出现保留环

  • 找适当的时机解除保留环,比如在不再需要调用块的时候,放弃块的持有。

41.多用派发队列,少用同步

_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);
- (NSString *)someString {
  __block NSString *localSomeString;
  dispatch_sync(_syncQueue, ^{
    localSomestring = somestring;
  });
  return localsomestring;
}

-(void)setSomeString:(NSString *)somestrin {
  dispatch_sync(syncQueue, ^{
    _someString = somestring;
  });
}
** 把设置操作与获取操作安排在序列化队列执行,这样的话,所有针对属性的访问操作都同步了。
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);

** 在队列中,栅栏块必读单独执行,不能与其他块并行。只对并发队列有意义。并发队列如果发现接下来要处理的块是个栅栏块,那么就一直要等当前的所有并发块都执行完毕,才会单独执行这个栅栏块。可以使用栅栏块来实现属性的Set方法。对属性的读取操作依然可以并行执行。

42.多用GCD,少用performSelector系列方法

  • performSelector系列方法在内存管理方面容易有缺失,它无法确定将要执行的选择子具体是什么,因而ARC编译器也就无法插入适当的内存管理方法。

43.掌握GCD及操作队列的使用时机

  • 使用NSOperation及NSOperationQueue

44.通过Diapatch Group机制,根据系统资源状况来执行任务

  • long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); timeout参数标识函数等待dispatch group执行完毕时,应该阻塞多久。如果执行dispatch group所需时间小于timeout,则返回0,否则返回非0值。此参与也可以去DISPATCH_FOREVER,这表示函数会一直等待dispatch group执行完。
  • void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);等dispatch group执行完毕之后,块会在特定的线程上执行。
  • void dispatch_apply(size_t iterations, dispatch_queue_t queue, void(^block)(size_t));该函数会反复执行一定的次数,每次传给块的参数值都会递增,从0开始,直至iterations-1。
    dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL);
    dispatch_apply(10, queue, ^(size_t i){
      //perform task
    });
    

    使用的队列可以是并发队列。

45.使用dispatch_once来执行只需运行一次线程安全代码

46.不要经常使用dispatch_get_current_queue

  • dispatch_get_current_queue已经废弃,应当只用做调试用。
  • 派发队列是按层级关系来组织的,所以无法单用某个队列对象来描述“当前队列”这一概念。

47.熟悉系统框架

48.多用块枚举,少用for循环

49.对自定义其内存管理语义collection使用无缝桥接

50.构建缓存时选用NSCache而非NSDictionary

* NSCache可以提供优雅的自动删减功能,而且时线程安全的。
* NSPurgeableData,是NSMutableData的子类,这个类实现了NSDiscardableContent协议。系统资源紧张时,可以把这个对象的那一块内存释放掉。可以访问isContentDiscarded查询相关内存是否已释放。
* NSPurgeableData加入NSCache,那么该对象为系统所丢弃时,也会自动从缓存中移除。

51.精简initialize与load的实现代码

* 对于加入运行期系统中的每个类(class)及分类来说,必定会调用此方法,而且仅调用一次。
* initialize和load区别initialize是惰性调用。initialize是线程安全的。
* 如果某个类没有实现initialize,但是其超类实现了,就会调用超类的initialize方法。load方法则不然。
* 无法在编译器预设的全局变量,可以放在initialize方法里初始化。比如数组

52.别忘了NSTimer会保留其目标对象

* NSTimer对象对保留其目标,知道计时器本身失效为止。调用invalidate方法可令计时器失效。
* 注意保留环。

原文地址:https://blog.csdn.net/qq_43613781/article/details/126643514

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_11707.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注