iOS学习笔记之OC基础

从2.29号开始看 BigNerd 出版的《Objective-C编程》,花了两天时间将 C 语言部分温习了一遍,由于我是计算机专业的毕业生,所以 C 系的语言还算靠谱,但毕竟做了一年 Web 开发,重点都放在 PHP 和其他 Web 开发知识上,所以关于 C 的知识也已忘却不少。无论如何,我还是认真的把 C 的基础过了一遍(PS:有个讨论苹果开发入门的知乎贴有人说这本书只需要一天时间,实在佩服!),并且跟着作者YY了一下 iOS 的开发工具 Xcode IDE,为什么YY呢,因为书上用的是 Mac 下的 Xcode,而我先前的C语言部分都是用的 Win 下的 Codeblocks,原因且看下面。

吐槽

因为我手头并不宽裕,只有一个 iPad mini 2,当时买来是为了看书看视频用的,而 MAC 呢,虽然我没有,但是我姐有,可是她白天上班要带到公司去,所以我们的计划就是我白天在家看书,晚上等电脑回来再敲代码。然而这几天晚上我都在熟悉 MAC 的使用以及……下载Xcode,是的,连我自己也没想到。在鬼畜的 iTunes 上下载4.5G的 Xcode 7.2.1 尝试了2个晚上,就看到菊花一直转啊转啊,也不知道进度多少,?下载无果后,我姐利用上班时间在 Apple 官网下载了最新的 Xcode 7.3 Beta 版,这个版本我之前在官网看到过,因为是 Beta 版,所以才没有下载。那好吧,既然下载了,那就安装吧……,安装完运行才提示 Xcode 7.3 只支持 OS X 10.11 以上,而我的系统还是 OS X 10.10.1,试问隔壁的Windows有按照系统小版本更新来支持工具的吗?那我想估计下载 Xcode 6 就可以了吧,然而翻遍官网也没找到 Xcode 6 或者其他不是最新版的 Xcode 的链接,最后通过Google找到了 StackOverflow上同样的问题:how-to-download-xcode-4-5-6-7-and-get-the-dmg-file ?,才知道要 访问Apple官网的这个页面,登陆 Apple ID,然后就可以下载Xcode的历史版本了。oh,妈妈咪呀,终于找到了你了,真是“皇天不负有心人”“踏波铁屑无觅处,得来全不费工夫。”“蓦然回首,那人却在灯火阑珊处”啊!好歹看见了一丝曙光,耗费昨晚一晚上,终于下载下来了 2.6 G 的 Xcode 6.4,等今晚上安装吧,这下应该是没问题的。结果晚上我姐回家说已经为此更新了系统。
吐槽结束,回到正题吧。
前面说过,我们这里省略 C 语言的部分,所以直接从13章开始。

OC-13章:对象

  • 对象和结构体类似,结构体中的成员在对象中称为实例变量(instance variable),对象和结构体的不同点在于:对象还可以包含一组函数。
  • 类负责描述特定类型的对象,一个类定义了一种对象。
  • 画对象图时,类一般用虚线,实例(对象)一般用实线
  • OC 头文件后缀为.h,其他代码后缀为.m
  • OC 用#import导入头文件,类似于 C 的#include,但是#import导入更有效率。
  • Foundation框架是 iOS 项目的一个基础框架,由多种类、函数、常量以及数据类型组成。
  • NSLog()函数类似于C语言的printf()函数,它与printf()的区别在于会先输出日期、时间、程序名称和进程ID,再输出实际内容。格式打印符%p会输出指针地址,%@会输出对象的‘描述信息’。
  • 在 OC 中,若要执行方法里的代码,首先需要发送一条消息给包含这个方法的对象或类
  • 消息发送:必须写在一对方括号中,并且必须包含接收方(receiver)和选择器(selector)。
    Message

  • 类方法(class method)会创建类的实例,并初始化实例变量,如date。
    实例方法(instance method)会提供实例中实例变量的信息,或是对实例的实例变量进行操作,如timeIntervalSince1970。

  • OC 区分大小写,以下为 OC 的语言命名习惯:
    • 指向实例的变量使用驼峰命名法(camel case),以小写字母开头,接下来每个单词首字母大写。
    • 方法的命名也使用驼峰命名法。
    • 类的名称以大写字母开头,接下来的单词继续使用驼峰命名法。
    • 很多苹果创建的类型和常量的命名也是前缀大写的驼峰命名法,如NSInteger,他并不是类,只是一种整型类型。

2016.3.5 12:46 更新


OC-14章:消息

  • 传递实参的消息,方法名后带有冒号:意味需要发送带有实参的消息,而且每个冒号后面都要传递参数。在写代码时,多个实参应该对齐冒号。
  • NSDate是时间类,NSCalendar是日历类。
  • 消息可以嵌套发送,系统会最先从最里面的消息由内之外依次执行。
  • 唯一必须以嵌套的形式连续发送的消息是alloc和init。
    每个类都有一个 alloc 方法,它能创建一个对象,并返回指向该对象的指针,通过 alloc 出来的对象,必须要经过初始化才能使用,它存放在内存里,但是无法接收消息,每个类也都有一个 init 方法,它用来初始化实例。eg.[[Class alloc] init]
  • nil 是不指向任何对象的指针,nil 值为0。在OC中向 nil 发送消息,没有任何影响。
    • 重点1:如果程序向某个对象发送了消息,但却没得到预期的结果,应先检查消息接收方是否为 nil。
    • 重点2:向 nil 发送消息,得到的返回值没有任何意义。
  • 声明指针时如不知道指针所指对象的准确类型,可以使用id类型。id 类型的含义是:可以指向任意类型的OC对象的指针,并且已经隐含了*的作用。eg.id example

OC-15章:对象与内存

  • 函数的帧在函数结束后会自动释放,堆里的对象不会自动释放。
  • ARC:自动销毁不被引用的对象的机制,自动引用计数(automatic reference counting)的简称。当对象对指向自己的指针计数为0时,程序便自动销毁该对象。
    如果不再需要某个对象,则可以将指向它的指针设置为 nil。

OC-16章:NSString

  • @"..."是OC中的一个缩写,代表根据给定的字符串创建一个 NSString 对象。这种缩写称为字面量语法(literal syntax)。
  • NSString 实例可以保存任意 Unicode 字符,如需插入非 ASCII 字符,则可以使用\u,后面加上该字符的 Unicode 编码。

    1
    NSString *str = [NSString stringWithFormat:@"I \u2661 You"];

2016.3.5 23:46 更新


OC-17章:NSArray

NSArray

  • NSArray 也可以用字面量语法来创建实例,数组的内容写在方括号,使用逗号分隔。eg:

    1
    NSArray *list = @[eleOne, eleTwo, eleTree];
  • NSArray 创建的是静态数组(static array),一旦 NSArray 实例被创建,就无法添加或删除数组里的指针,也无法改变数组的指针顺序。
  • NSArray 中的指针是有序的,并可以通过相应索引来存取,索引从0开始。这一点和C语言类似。
  • count方法会返回 NSArray 对象中所含指针的个数。
  • 遍历数组:

    • 标准方式

      1
      2
      3
      for(init; condtion; step){
      //操作数组元素
      }
    • OC的快速枚举

      1
      2
      3
      for(variable in array) {
      //操作数组元素
      }
  • 旧式数组方法:

    • 还没有字面量语法的时候(用@创建数组),只能用 arrayWithObjects: 类方法来创建 NSArray 实例。eg:

      1
      NSArray *list = [NSArray arrayWithObjects:eleOne, eleTwo, eleThree, nil];

      最后的nil告诉方法停止运行。

    • 还没有下标语法的时候,一般使用ObjectAtIndex来访问数组中的指针。eg:

      1
      NSLog(@"%@",[list objectAtIndex:2]);

NSMutableArray

  • NSMutableArray 是 NSArray 的子类。
  • NSMutableArray 创建的是动态数组(dynamic array),和 NSArray 实例不同的是,它可以添加、删除或对指针重新进行排序。
    • addObject:方法给 NSMutableArray 在尾部添加对象。
    • insertObject:atIndex:在指定位置添加对象。
    • removeObjectAtIndex:删除数组中指定位置的对象,数组中的对象个数会随之减少。
    • 如果遍历数组时需要添加或删除指针,则需要使用标准的 for 循环。

2016.3.6 23:10 更新


OC-18章:自定义类

  • OC的头文件.h又称接口文件(interface file),包含实例变量和方法的声明。OC的.m文件称为实现文件(implementation file),包含所有方法的代码实现。
  • OC 头文件@interface开始,以@end结束。

    • 在花括号{}里声明实例变量,实例变量以下划线_开始,后跟实例变量的名字。书上说明_没有特别语法含义,仅仅是易于区分实例变量和局部变量。
    • 声明实例方法以减号-开始(类方法以加号+开始?),后面跟方法声明。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #import <Foundation/Foundation.h>
    @interface ClassName : ExtendsClassName
    {
    //声明实例变量
    int _stockNumber;
    }
    //声明方法,或者属性
    // - (返回类型)函数名:参数;
    - (float)valueStock:(float)rate;
    @end
  • OC 实现文件@implementation开始,以@end结束。

    1
    2
    3
    4
    5
    6
    7
    #import "ClassName.h"
    @implementation ClassName
    //实现方法
    - (float)valueStock:(float)rate {
    //code
    }
    @end
  • 存取方法(accessor method):取方法(getter method)用去掉下划线前缀的实例变量命名。存方法(setter method)用set后跟去掉下划线前缀的实例变量命名,并且要遵循驼峰命名。eg:

    1
    - (void) setHeightInMeters:(float)h;
  • <>""包含头文件的区别:<>包含系统头文件,""包含当前项目头文件。

  • self是一个指针,指向当前对象。可以作为实参传递给其他方法。
  • OC没有命名空间(namespace),所以为了避免同一个项目中名字冲突,推荐使用三个或者三个以上的字母最为类前缀。

OC-19章:属性

OC有一条便捷途径,可以简化存取方法的过程,该途径称为属性

  • 声明属性:可以取而代之18章在花括号中声明实例变量,且声明属性不用写在花括号里,而是和声明方法在同一个级别。
    声明属性以@property开始,后跟属性的特性、类型和名称。

    1
    @property (nonatomic) float heightInMeters;

    声明属性的优点:

    1. 让头文件更简洁。
    2. 减少代码输入。
    3. 最重要的是,编译器会帮你声明和实现存取方法。
  • 属性的特性:属性的声明可以有一个或多个属性特性(property attributes),属性特性告诉编译器关于属性行为的更多信息,多个属性特性用逗号,分隔。

    1. 属性的特性分为原子性(atomic)和非原子性(nonatomic),默认为原子性。
    2. 属性的另一种特性:
      • readonly(只读):只有取方法,没有存方法。
      • readwrite(读写):默认值,既有存方法,也有取方法。
      • copy(可拷贝):每当声明一个指向NSString或NSArray的对象的属性时,都需要设置copy属性。
      • 还有更多……
  • 点号调用存取方法:dot notation 是苹果公司提供的一种快捷调用存取方法的语法。它和获取结构体成员方法一样,但是不同之处在于,使用 dot notation 实际是在发送消息。

    1
    2
    3
    //下面的代码对于属性的存取是等价的
    object.var = 10;
    [object setVar:10]; //setVar是编译生成的存方法

测试发现,也可以用点号调用其他非存取方法。

OC-20章:继承

  • 本书所有对象(无论间接的还是直接的)都继承自NSObject。

    1
    2
    @interface ClassName : NSObject 
    @end
  • 当有属性指向某个对象的时候,需要对内存进行管理。

  • 子类的实例可以毫无疑问的代替父类的实例,因为它继承了父类的所有东西。
  • 覆盖方法:覆盖方法只能改变方法的实现,而无法改变它的声明方式,方法名称、返回类型以及实参类型都必须保持相同。
  • 子类可以利用父类的实现,而不是完全替换,不过需要用到 super 指令。super 的含义是:运行指定的方法,但是从对象的父类开始查找与之匹配的实现。

    1
    2
    //在实现方法代码中
    [super parentClassMethod]; //运行父类
  • NSObject 虽然拥有很多方法,但是只有一个实例变量: isa 指针。任何一个对象的 isa 指针都指向创建该对象的类.
  • 给对象发送消息的时候,对象就会查询是否有该消息名的方法。搜索会通过 isa 指针找到该对象的类并查询,如果没有匹配该消息名的方法,如果没有,就会继续查询它的父类。依次类推,对象会沿着继承链向上查询,直到找到匹配消息名的方法,或者到达继承链的顶端(NSObject)为止,如果还是没找到,就报错。
  • %@description 方法的转换:处理%@时,程序会先向相应的指针变量所指的对象发送 description 消息,description 方法会返回一个描述类实例的字符串。description 是一个NSObject方法,所以所有对象都有这个方法,类可以覆盖 description 方法来定义类实例的描述。eg:

    1
    2
    3
    - (NSString *)description {
    return [NSString stringWithFormat:@"..."];
    }

2016.3.9 22:50 更新


OC-21章:对象实例变量及属性

  • 对象实例变量的三种常见用途:
    • 对象属性:指向某个单一的、代表某个值的对象,如NSString对象。
    • 一对一关系:指向单个复杂对象的指针。
    • 一对多关系:指向某个collection类的实例的指针。如NSMutableArray实例。
  • 任何一个对象不会在其内部保存其他对象,而只会在内部保存相应对象的地址。与“包含其他对象”相比,这样会产生两大副作用。
    • 单个对象可能会扮演多个角色。
    • 导致产生大量独立的对象,耗尽程序的可用内存。
  • 为了解决上个问题的副作用,OC提出了对象所有权(object owership)概念,当A对象拥有某个对象实例变量B时,称A拥有了B指向的对象。当某个对象的拥有方个数为0时,可以判定程序不再需要该对象,从而释放该对象。
  • 有一个NSObject方法:dealloc,如果类覆盖了该方法,那么当类的实例被释放时,就会执行dealloc方法。
  • 一对多关系是由collection对象(如数组)和其包含的对象组成的。
    • 当在collection对象中加入某个对象的时,会在collection对象中保存指向该对象的指针,并成为该对象的拥有方。
    • 当从collection中移除某个对象时,会删除指向该对象的指针,并不再是该对象的拥有方。
  • @class ClassName引用某个头文件时,和#import不同的是,编译器不会查看文件的全部内容,因此处理速度更快,这样是因为编译器不需要知道实现细节就能处理文件中的所有声明了。
  • 创建collection对象的时机有两个:
    • 创建本地对象时。
    • 需要使用相应collection对象时(延迟创建机制)。
  • 如果程序有用不到的但有没有释放的对象,就称程序有内存泄露(memory leak)。

OC-22章:类扩展

  • 涉及实现细节的属性或方法最好在类扩展(class extension)中声明,类扩展是一组私有的声明,只有类和该类的实例才能使用在类扩展中声明的属性、实例变量或方法。
  • 通常,类扩展是添加在类实现文件中的,并且是位于@implementation之上的,并且和头文件中一样要用@interface ... @end包起来,但是类名字后面是跟一对括号(),声明方法都一样。eg:

    1
    2
    3
    4
    5
    6
    @interface ClassName()
    //...类扩展声明内容放在这里...
    @end
    @implementation ClassName
    //...原来实现方法的代码...
    @end
  • 可用类扩展隐藏可变属性。比如 NSMutableArray。

  • 子类无法获取父类的类扩展。
  • 在类的头文件中声明属性的时候,其他对象只能看到属性的存取方法,对象(包括子类)无法直接获取属性声明生成的实例变量。

2016.3.10 22:05 更新


OC-23章:避免内存泄露

  • “互相拥有”的所有权关系导致相关的对象都无法被释放。这种情况称为强引用循环(strong reference cycle),强引用循环是导致内存泄露的常见原因。
  • 通过 弱引用 (weak reference)可以解决强引用循环问题。在声明属性的时候在特性中采用weak关键字,eg:

    1
    @property (nonatomic, weak) Object *pointer;
  • 如果需要明确的将指针变量声明为弱引用,则可以标注__weak,eg:

    1
    __weak Object *pointer;
  • 为了避免强引用循环,通常需要遵守此规则:父对象拥有子对象,但是子对象不拥有父对象。
  • 弱引用有自动置0特性,当某个由弱引用指向的对象被释放时,相应的指针变量会被归零(zeroed),即赋为nil。
  • 总结:强引用会保留对象的拥有方,使其不被释放。而弱引用则不会保留,因此标为弱引用的实例变量与属性指向的对象可能会消失。
  • 在没有ARC之前,都是采用手动引用计数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // Code block
    [anObject retain]; //获取anObject的拥有权
    [anObject release]; //释放anObject的拥有权
    [anObject autorelease]; //自动释放anObject的拥有权,自动一般在autorelease池(对象)被排干(drain)的时候;

    //Another code block
    //手动创建autorelease池
    NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init];
    //coding here
    [arp drain] //排干autorelease池
    //--------------
    //Objective-c加入ARC,也加入了用于创建autorelease池的新语法。
    @autoreleasepool { //创建autorelease池(对象)
    // coding here
    } //autorelease池已经被排空
  • 理解内存管理的技巧“从局部的角度,以分类为分界”。

OC-24章:Collection 类

NSSet/NSMutable(集合)

  • NSSet对象所包含的内容是无序的,而且在NSset对象那个中,某个对象只能出现一次。
  • NSset对象的最大用处就是检查某个对象是否存在。
  • NSset对象也分为可对指针进行修改和不可对指针进行修改两类:NSSet对象是不能对指针进行修改的,对象创建好后就不能对其中的指针进行添加或删除等操作,而NSMutableSet是它的子类,它是动态可变的。
  • NSset对象中的对象是无序的,所以不能通过索引来访问。
  • 如果程序要检查某两个对象是否相等,可以用NSObject类中的isEqual:方法。
  • 相同的变量一定是相等的,而相等的变量不一定相同。

NSDictionary/NSMutableDictionary(字典)

  • NSDictionary是一组键-值对(key-value pair)集合,键通常是字符串,值可以是任意类型的对象。字典对象中的键都是独一无二的,字典对象所保存的键-值对是无序的。字典的字面量语法由@{}组成,键值对之间用逗号,分割。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //创建字典
    NSDictionary *dic = @{
    @"key-1" : @0, //值是数字对象
    @"key-2" : @[@"Lua", @"xiba"], //值是数组对象
    @"key-3" : @"abc", //值是字符串对象
    };
    //从字典取值
    dic[@"key-2"];
    //还没引入下标的用法
    [dic setObject:"key-x" fouKey:"Hello"]; //添加字典对象那个
    [dic objectForKey:@"key-x"]; //从字典取值

不可修改对象

  • 问:前面讲的 collection 都有不可更改和可更改的,那么为什么要提供不能修改的 collection 呢?
    答:使用不能修改的 collection 可以节约内存提高性能,因为它永远无法拷贝。而对于可修改对象,则可能发生这样一种情况:程序中的其它代码可能在你使用这个对象时修改这个对象的内容。为了避免这种情况,就需要复制一份私有拷贝。而每个程序的代码都有可能做一份私有拷贝,这样就有可能会有多份一样的对象拷贝。

    比如 NSArray 的 copy 方法实际上不会做任何额外的操作,仅仅返回指向自身的指针而已。而 NSMutableArray 的 copy 方法则会制作一份自己的拷贝,并返回指向新数组的对象的指针。

  • 很多类都有不可修改对象,这些类都有可修改的子类(一般在中间加入Mutable),但是 NSDateNSDate 只有不可修改的版本

数组排序

  • 不可修改的数组不能排序。
  • 排序描述对象(sort descriptor)包含两个信息:一个是数组中的对象的属性名,而是根据该属性是要升序(ascending)还是降序(descending)。eg:

    1
    2
    NSSortDescriptor *desc = [NSSortDescriptor sordDescriptorWithKey: @"key" ascending:YES]; //key可以是方法名,也可以是实例变量或属性的名字。
    [mutableList sortUsingDescriptors: @[desc]];//将mutableList数组按照descriptor排序后,结果依然放在mutableList中。

过滤

  • 对collection进行过滤的时候,程序将对collection对象和一条逻辑语句进行比较,得到一个合成的collection,这个collection包含满足这条语句的对象:

    1
    2
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"value > 70"]; //设置过滤条件
    NSArray *result = [list filteredArrayUsingPredicate:predicate]; //过滤
  • Predicate对象的条件语句可以支持非常复杂的格式。

collection 注意事项

  • 向collection对象加入某个对象时,collection对象会成为该对象的拥有方,如果从collection对象中移除该对象,collection对象就不再是该对象的拥有方了。
  • 本章节提到的collection只能保存对象,如果要保存C语言的基本类型,可以创建相应的对象进行封装。NSValue 实例可以用来保存任意的数量值。
  • 本章节提到的collection对象都不能保存 nil ,如果要将“空”加入collection对象,则可以使用NSNull类,它只有一个实例,代表“空”。eg:

    1
    [collection addObject:[NSNull null]];

OC-25章:常量

  • 将代码中不变的值称为常量
  • OC中可以通过 #define全局变量 来定义常量。
  • 在OC中π的常量是M_PI
  • 通过 #define,不仅可以替换代码中某个特定值,还可以构建类似函数的代码段,称为(macro)。
  • NSLocale 实例可以保存针对不同地理位置的相关信息。
  • extern 关键字导入其他文件中的定义。
  • 苹果引入了一种新的enum声明语法:NS_ENUM(),它有两个参数,分别是数据类型和名字。
  • 在 #define 和 全局变量 中更倾向于使用全局变量来定义常量。

OC-26章:NSString和NSData写入文件

  • 将NSString对象写入文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    NSMutableString *str = @"Hello world!\n";
    NSError *error=nil; //用于处理错误的参数
    BOOL success = [str writeToFile:@"/tmp/test.txt atomiclly:YES encoding:NSUTF8StringEncoding error:&error"];
    if (!success) {//写入文件失败
    NSLog(@"%@", [error localizedDescription]); //输出时要格式化错误信息
    return 1;
    }
    //否则写入成功
    NSLog(@"Success!");
  • 将文件读入NSString对象

    1
    2
    3
    4
    5
    6
    7
    NSError *error=nil;
    NSString *str = [[NSString alloc] initWithContentsOfFile:@"/tmp/test.txt" encoding:NSASCIIStringEncoding error:&error];
    if (!str) {
    //读取失败
    } else {
    //读取成功
    }
  • 将NSData对象写入文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    NSError *error=nil;
    NSURL *url = [NSURL URLWithString:@"http://google.com/images/logo/ps_logo.png"]; //给一个有效图片地址就行
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSData *data = [NSURLCollection sentSynchronousRequest:request returningResponse:NULL error:&error];
    if (!data) {
    //读取失败
    } else {
    //读取成功
    }
  • 读取文件存入NSData对象

    1
    NSData *readData = [NSData dataWithContentOffile:@"/tmp/test.txt"];
  • 寻找特别目录:苹果公司创建了一个函数告诉你正确的目录

    1
    NSArray *deskTop = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);//NSDesktopDirectory是桌面目录,还有其他目录常量可以查看文档

2016.3.17 16:15 更新


OC-27章:回调

  • 回调 (callback)就是将一段可执行的代码和一个特定的事件绑定起来,当特定的事件发生时,就会执行这段代码。在OC中,有四种可实现回调的方法:

    • 目标-动作对:在程序开始等待前,要求“当事件发生时,向指定的对象发送某个特定的消息”。这里接收消息的对象是目标(target),消息的选择器(selector)是动作(action)。
    • 辅助对象:在程序开始等待前,要求“当事件发生时,向遵守相应协议的辅助对象发送消息”,委托对象(delegate)和数据源(data source)是常见的辅助对象。
    • 通知(notification):苹果提供了一种称为通知中心的对象。在程序开始等待前,告知通知中心“某个对象正在等待某些特定的通知,当其中某个通知出现时,向指定的对象发送特定的消息”。
    • Block对象:Block 是一段可执行的代码。当程序开始等待前,声明一个Block对象,当事件发生时,执行这段Block对象。
  • 事件驱动的程序需要有一个对象,专门等待事件的发生。NSRunLoop实例会在特定的事件发生时触发回调。

  • @selector语句用来传递动作消息的名称给相应方法。
  • 可以在声明一个变量前使用__unused修饰符标记,消除编译器“变量未使用”警告。
  • NSURLConnection在异步模式时,不会一次性发送全部数据,他会发送块状的数据,并多次发送。
  • 协议(protocol)是一系列方法声明,辅助对象可以根据协议实现相应方法
  • 如何选择除了 Block 外的三种回调方法:
    • 对于只做一件事情的对象,使用目标-动作对。
    • 对于功能更复杂的对象,使用辅助对象。
    • 对于要触发多个回调的对象,使用通知。
  • 创建的对象拥有一个指向回调对象的指针,而这个回调对象的指针指向你创建的对象。就陷入了一个强引用循环,这两个对象都无法释放。所以编写回调代码时,应该遵循以下规则:

    • 通知中心不拥有观察者。如果将某个对象注册为观察者,那么通常应该在释放该对象将其移出通知中心。
    • 对象不拥有委托对象或数据源对象。如果某个新创建的对象是另一个对象的委托对象或数据源对象,那么该对象应该在其dealloc方法中取消相应的关联。
    • 对象不拥有目标。如果新创建的对象是另一个对象的目标,那么该对象应该在其dealloc方法中取消相应的关联。
  • 如果使用方法的实际名称进行查询,那么查询速度会很慢,为了提速,编译器会为了每个其解除过的方法附上一个唯一的数字。这个数字称为选择器,通过编译指令@selector可以得到与方法名相对应的选择器。

OC-28章:Block对象

  • ^开始表示这段代码是一个 Block 对象。Block 对象也可以有实参和返回值。
  • Block 对象可以被当成一个实参来传递给可以接收 block 的方法。
  • 声明一个 Block 时,要参考要使用该 Block 对象的方法期望的 Block 类型。
  • 对于有返回值的 Block 对象,可以像调用函数那样调用 Block 对象,然后使用其返回值。
  • 匿名 Block 对象是可以传递给方法的 Block 对象的,而不需要先赋值给变量。
  • 当在 Block 中使用在 Block 对象外声明的变量(外部变量)时,相应的 Block 对象会捕获这些变量。

    对基本类型的变量,捕获意味着程序会拷贝变量的值,并用 Block 对象中的局部变量保存。对指针类型的变量, Block 对象会使用强引用,这意味着凡是 Block 对象使用到的对象,都会被保留,所以在相应的 Block 对象被释放前,这些对象一定不会被释放(这也是block对象和函数的区别,函数无法做到)。

  • 在 Block 对象中捕获的外部变量是常数,程序无法修改变量所保存的值。如果需要在 Block 对象中修改某个外部变量,则可以在声明外部变量前,加上__block关键字。

    1
    2
    __block int count=0;
    ^{ count++; };
  • 在 Block 对象中使用self时,又会陷入强引用循环,为了打破它,可以在 Block 对象外声明一个__weak指针,然后指向 Block 对象中要使用的 self,然后在 Block对象中使用新的指针。

    1
    2
    3
    4
    __weak Object *weakself = self; //弱引用
    myBlock = ^{
    NSLog(@"Object: %@", weakself);
    };

    然而是由于是弱引用,self指向的对象有可能在运行时被释放。可以在 Block 对象中创建一个对self的局部强引用。

    1
    2
    3
    4
    5
    __weak Object *weakself = self; //弱引用
    myBlock = ^{
    Object *innerSelf = weakself; //局部强引用
    NSLog(@"Object: %@", innerSelf);
    };
  • 如果直接在 Block 对象中使用实例变量,那么 Block 对象会捕获 self,而不会捕获实例变量。这样又会陷入强引用循环,所以不要直接存取实例变量,尽量使用存取方法。

    1
    2
    3
    4
    5
    6
    7
    __weak Object *weakself = self; //弱引用
    myBlock = ^{
    Object *innerSelf = weakself; //局部强引用
    NSLog(@"Object: %@", innerSelf);
    NSLog(@"value is %@", _value); //不推荐:Block 对象会捕获self
    NSLog(@"value is %@", innerSelf.value);// 推荐使用存取方法
    };

OC-29章:协议

  • 协议可以为一个对象指定角色。
  • 协议是一组方法声明,一些是必须的,一些是可选的。如果要某个对象扮演特定的角色,就一定要实现必须的方法,选择实现可选方法。
  • 在创建要遵守某个协议的新类时,要在头文件中进行说明,eg:

    1
    2
    3
    @interface TerrificView: UIViewController <ProtocolNameProtocolName> //该类继承自UIViewController类,并且遵守尖括号中列举的协议,多个协议用逗号隔开
    ...
    @end
  • 如果给一个对象发送消息,而这个对象没有相应的方法,程序会崩溃。如果在遵守某协议的类中没有实现某个可选方法,也是如此。为了避免这种情况,可以使用respondsToSelector:询问对象是否实现了某个方法,该方法在NSObject中实现了,返回值是BOOL型。

    1
    2
    3
    4
    ...
    if ([_data respondsToSelector:@selector(totalValue)]) {
    [_data totalValue];
    }

OC-30章:Proper List 格式

一个NSArray里面放上N个NSDictionary就组成了P-List格式,再输出到后缀.plist的文件中即可,好像没什么重点……

——— 结束de分割线 ———

到今天为止,Objective-C基础部分就学习结束了,这篇笔记也更新结束了,因为中途情绪比较低落,看书容易走神,所以速度还是有点慢。希望后面能好起来。

avatar

神无

舍悟离迷,六尘不改。