Objective-C 基本知识点

1. Cocoa 和 Cocoa Touch 的区别。

  • 相同点:都包含运行时的核心框架;
  • Cocoa:包含 Foundation 和 AppKit 框架,可用于开发 Mac OS X 系统的应用程序。
  • Cocoa Touch:包含 Foundation 和 UIKit 框架,可用于开发 iOS 系统的应用程序。

2. 直接访问实例变量和用点语法访问属性的优劣。

  • 直接访问实例变量的优势:不需要通过“方法派发系统”,所以直接访问实例变量速度更快。在这种情况下,编译器所生成的代码会直接访问保存对象实例变量的那块内存。
  • 直接访问实例变量的劣势:①无法触发 KVO“键值观测”。 ②无法通过重写set 或者 get 方法来断点定位错误。③绕过了为相关属性所定义的“内存管理语义”。④懒加载的属性,将无法得到加载。
  • 结论:①类内部读取数据的时候,直接访问实例变量,来提高速度 ②类内部设置属性值的时候,使用点语法来写。③在需要惰性初始化的时候,使用点语法来读取属性数据。

3. 字符串存储位置

  • 如果是通过字符串常量创建,那么字符串对象存储在常量区中。
  • 如果是通过 alloc initWithFormat / stringWithFormat 创建,那么字符串对象存储在堆区中。(stringWithFormat,不加变量,小于 9 个字符,还是在常量区)

4. 协议与委托代理(https://juejin.im/post/5b69e74cf265da0f9b4e0ce3

  • 协议(Protocol):一般被用来定义一套公用的方法接口,而不实现这些方法。

  • 委托代理 Delegate:代理实际上去一种设计模式,在 A 类中定义委托代理属性,在实例化后让 B 类对象成为其委托代理。这样就可以通过 A.delegate 来调用 B 中的方法了。

5. 分类(Category)和类扩展(Extension)(https://www.jianshu.com/p/9e827a1708c6

分类的情况如下:

  • 分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。所以< 原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过其它方式添加属性> ;
  • 分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告);
  • 可以在分类中访问原有类中.h中的属性;
  • 如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为 分类 > 本类 > 父类。因此在开发中尽量不要覆盖原有类;
  • 如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。

类扩展的情况如下:

  • 为一个类添加额外的原来没有变量,方法和属性
  • 一般的类扩展写到.m文件中
  • 一般的私有属性写到.m文件中的类扩展中

两者的区别和联系如下:

  • 类别中原则上只能增加方法(能添加属性的的原因只是通过runtime解决无setter/getter的问题而已);
  • 类扩展不仅可以增加方法,还可以增加实例变量(或者属性),只是该实例变量默认是@private类型的(用范围只能在自身类,而不是子类或其他地方);
  • 类扩展中声明的方法没被实现,编译器会报警,但是类别中的方法没被实现编译器是不会有任何警告的。这是因为类扩展是在编译阶段被添加到类中,而类别是在运行时添加到类中。
  • 类扩展不能像类别那样拥有独立的实现部分(@implementation部分),也就是说,类扩展所声明的方法必须依托对应类的实现部分来实现。
  • 定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有的。类扩展是在 .m 文件中声明私有方法的非常好的方式。

6. block (https://www.jianshu.com/p/b74598269140)

  • 声明一个 Block:
int (^block)(int, int) = (^)(int x, int y){
  return x + y;
}
  • block 和外部局部变量的关系 正常情况下,block 可以读取但是无法改变外部局部变量的值。如果需要修改需要加上 __block 来修饰。
  • block 和全局变量的关系: block 可以在内部直接改全局变量的值。
  • 在xcode里面,可以输入inlineBlock获取Block的基本写法。
// person.h
@property(nonatomic, copy) void(^eat)(NSString *);

- (void)eatWithBlock:(void(^)(NSString *))block;

- (BlockPerson *(^)(NSString *))eatCallBack;

// person.m
- (void)eatWithBlock:(void (^)(NSString *name))block {
    block(@"哈哈哈");
}

- (BlockPerson *(^)(NSString *name))eatCallBack {
    return ^(NSString *name){
        NSLog(@"block 作为返回值");
        return self;
    };
}

//  外部调用
BlockPerson *person = [BlockPerson new];
    person.eat = ^(NSString *name){
        NSLog(@"这个是 block 作为属性");
    };
    person.eat(@"ddd");
    
    [person eatWithBlock:^(NSString *name){
        NSLog(@"这个是 block 作为参数");
    }];
    
    // block 作为返回值(返回值是 block 且 返回值block的返回值是对象本身 且 方法没有参数::::这种情况可以实现链式调用编程语法)
    [person eatCallBack](@"ddd");
    person.eatCallBack(@"ddd").eatCallBack(@"xx");

6. 栈block 堆block 全局block(https://www.jianshu.com/p/4076fc8005c7)

  • 这三种 block 说的是存储区域不同,需要注意的是栈 block 存在栈空间,创建的对象随时可能被销毁,再次调用将会造成程序崩溃。
  • ARC 模式下,系统会自动对栈 block 进行 copy,让它变成堆 block。所以在 ARC 模式下,block 用 strong 或者 copy 修饰,效果是一样的。
    void (^DemoBlock)(void) = ^{
        NSLog(@"DemoBlock"); // 内部没调用外部变量时(或者调用全局区变量),存在在全局区
    };
    NSLog(@"%@",DemoBlock); // <__NSGlobalBlock__: 0x10359d178>
    
    int a =  100;
    void (^DemoBlock2)(void) = ^{
        NSLog(@"%i",a); // 调用了外部变量,存在堆区
    };
    NSLog(@"%@",DemoBlock2); // <__NSMallocBlock__: 0x6000003bf900>
    
    void (^DemoBlock3)(void)  = [DemoBlock2 copy];
    
    NSLog(@"%@",DemoBlock3); // <__NSMallocBlock__: 0x6000003bf900>

7. 什么是 Runtime

  • 因为 Objective-C 是一门动态语言,它的会把一些工作从编译期推迟到运行期决定。也就是说只有编译器是不够的,还需要一个运行期系统(Runtime system)来执行编译后的代码,这就是 Objective-C Runtime 系统存在的意义,它是整个 Objc 运行框架的一块基石。

8. objc_msgSend 消息发送过程

  1. 拿着选IMP 去类的 cache 列表里面找,找到了就执行函数。
  2. 如果 cache 里面没找到,就去方法分发列表里面找,没找到就去父类里面找,找到就执行,并且存储到 cache 里面,方便下次调用。
  3. 如果父类 NSObject 都没有找到,就要方法动态解析阶段(重定向,转发,动态绑定)

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

  1. Objective-C 里面没有内置的命名空间概念,所以我们只能变相使用前缀来尽量减少命名冲突(naming clash)。
  2. 如果自己开发的库使用到了第三方库,应该为使用的第三方库都加上自己的命名前缀。
  3. 默认情况下,苹果保留了 2 个字符的前缀,所以我们开发尽量使用三个字符的前缀。

10. 尽量使用不可变对象

  1. 尽量创建不可变对象。
  2. 如果对象外部只读,内部可修改,可以在类扩展上重新把 readonly 属性定义为 readwrite 属性。
  3. 对象中如果有可变的 collection ,尽量不要直接把 collection 暴露出去,而是添加额外的修改方法来供给别人调用。

11.为私有方法添加前缀

  1. 尽量给私有方法添加前缀,使得和共有方法区分开来。
  2. 不要使用单个_(下划线)来作为私有方法前缀,因为这是苹果的默认做法,容易造成覆盖一些苹果自带的方法,导致不可预期的 bug。

12.异常处理

  1. 只有发生了可使整个应用程序崩溃的严重错误时,才应使用异常。
  2. 在错误不那么严重的情况下,可以指派“委托方法”(delegate method)来处理,也可以把错误信息放在 NSError 对象里,经由“输出参数”返回给调用者。

13.分类 Category

  1. 把封装数据所用的全部属性都定义在主接口里。
  2. 在 ”class-continuation分类“之外的其他分类中,可以定义存取方法,但是尽量不要定义属性。

14. ”class-continuation 分类“

  1. 通过 ”class-continuation 分类“ 向类中新增实例变量。
  2. 如果某属性在主接口中声明为”只读“,而类的内部又要用设置方法修改此属性,那么就在”class-continuation 分类“中将其扩展为”可读写“。
  3. 把私有方法的原型声明在”class-continuation 分类“里面。
  4. 若想使类所遵循的协议不为人所知,则可于”class-continuation 分类“中声明。

15. 协议 Protocol

  1. 协议可在某种程度上提供匿名类型。具体的对象类型可以淡化成遵从某协议的 id 类型,协议里规定了对象所应实现的方法。
  2. 使用匿名对象来隐藏类型名称(或类名)
  3. 如果具体类型不重要,重要的是对象能够相应(定义在协议里的)特定方法,那么可以使用匿名对象来表示。

16. 内存管理

  1. 引用计数机制通过可以递增递减的计数器来管理内存。对象创建好之后,其保留计数至少为 1 。 若保留计数为正,则对象继续存活。当保留计数为0 时,对象就被销毁了。
  2. 在对象生命期中,其余对象通过引用来保留或释放此对象。保留与释放操作分别会递增及递减保留计数。

17. ARC的好处

  1. 有了 ARC 之后,程序猿就无须担心内存管理问题了。使用 ARC 编程,可省去类中许多”样板代码“。
  2. ARC 管理对象生命周期的办法基本上就是:在合适的地方插入”保留“ 以及 ”释放“ 操作。在 ARC 环境下,变量的内存管理语义可以通过修饰符指明,而原来需要手工执行”保留“以及”释放“操作。
  3. 由方法所返回的对象,其内存管理语义总是通过方法名来提醒。ARC 将此确定为开发者必须遵守的规则。
  4. ARC只负责管理 Objective-C 对象的内存。尤其要注意:CoreFoundation 对象不归 ARC 管理,开发者必须适时调用 CFRetain、CFRelease。

18. dealloc 方法里面可以做哪些事情,不该做哪些事情。

  1. 在 dealloc 方法里面,应该做的事情就是释放指向其他对象的引用,并取消原来订阅的”键值观测“KVO 或者 NSNotificationCenter 等通知,不要做其他事情。
  2. 如果对象持有文件描述符等系统资源,那么应该专门编写一个方法来释放此种资源。这样的类要和其他使用者约定:用完资源后必须调用 close 方法。
  3. 执行异步任务的方法不应在 dealloc 里调用;只能在正常状态下执行哪些方法也不应在 dealloc 里调用,因为此时对象已经处于正在回收的状态了。

19. weak 的由来和作用

  1. 将某些引用设置为 weak,可以避免出现”保留环“。
  2. weak 引用可以自动清空,也可以不自动清空,自动清空(autonilling)是随着 ARC 而引入的新特性,由运行期系统来实现。在具备自动清空功能的弱引用上,可以随意读取其数据,因为这种引用不会指向已经回收过的对象。
  3. 对比 unsafe_unretained 属性,这个属性就容易造成访问到已经释放的地址,而导致程序崩溃。

20. 自动释放池

  1. 自动释放池排布在栈中,对象收到 autorelease 消息后,系统将其放入最顶端的池子里。
  2. 合理运用自动释放池,可以降低应用程序的内存峰值。
  3. @autoreleasepool 这种新式写法能创建出更为轻便的自动释放池。

21. 僵尸对象(NSZombieEnable)

  1. 系统在回收对象时,可以不将其真的回收,而是把它转化为僵尸对象。通过环境变量 NSZombieEnable 可开启此功能。
  2. 系统会修改对象的 isa 指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够相应所有的选择子,相应方式为:打印一条包含消息内容及其接收者的消息,然后终止应用程序。

22. 使用 typedef 重新定义块类型

  1. 以 typedef 重新定义块类型,可令块变量用起来更加简单。
  2. 定义新类型时应遵从现有的命名习惯,勿使得其名称与别的类型相冲突。
  3. 不妨为同一个块签名定义多个类型别名。如果要重构的代码使用了块类型的某个别名,那么只需要修改相应的 typedef 中的块签名即可,无需修改其他 typedef。

23. 编程思想:块 handler 的使用

  1. 在创建对象时,可以使用内联的 handler 块将相关业务逻辑一并声明。
  2. 在有多个实例需要监控时,如果采用委托模式,那么经常需要根据传入对象来切换,而若改用 handler 块来实现,则可以直接将块与相关对象放在一起。
  3. 设计 API 时如果用到了 handler 块,那么可以增加一个参数,使调用者可以通过此参数来决定应该把块安排在哪个队列上执行。

24. 编程思想:块容易造成保留环

  1. 如果块所捕获的对象直接或者间接地保留了块本身,那么就得当心保留环的问题。
  2. 一定要找个适当的时机解除保留环,而不能把责任推给 API 的调用者。

25. NSOperation 优于 GCD 的地方

  1. 具有取消操作。
  2. 需要指定多个操作之间的依赖关系。
  3. 需要通过键值观测机智监控 NSOperation 对象的属性。
  4. 需要指定操作的优先级。
  5. 重用 NSOperation 对象。

26. GCD 相关

dispatch_group_wait : 如果想令数组中的每个对象都执行某项任务,并不想等待所有任务执行完毕,那么就可以使用这个 GCD 特性来实现。

dispatch_group_notify : 若当前线程不应阻塞,则可以用 notify 函数来取代 wait ;

27. GCD 相关

经常需要编写“只需执行一次的线程安全代码”(thread-safe single-code execution)。通过 GCD 所提供的 dispatch 函数,就很容易实现此功能。

标记应该声明在 static 或 global 作用域中,这样的话,在只需执行一次的块传给 dispatch_once 函数时,传进去的标记也是相同的。

28. GCD 相关

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容