一、类和对象

1.OC语言是C语言的扩充,并且OC是iOS和OS X操作系统的编程语言。

①具备完善的面向对象特性:

封装:将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内

继承:子类自动共享父类数据结构和方法的机制,这是类之间的一种关系

多态:指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果

②内存管理机制有GC(垃圾回收机制)和RC(引用计数机制【MRC和ARC】),但是iOS支持两种内存管理方式:ARC和MRC。

③面向对象的核心思想是类和对象

2.类:具有相同特征和行为的事物的抽象。【是对象的类型】

①类的定义分为:接口部分和实现部分

<1>接口部分:对外声明类的特征和行为。(.h文件中)

标志:@interface...@end【作用;声明类名,类的继承(父类),遵循的协议,实例变量和方法(特征和行为)】

<2>实现部分:行为的具体实现。(.m文件中)

标志:@implementation...@end【方法的实现】

3.对象:类的实例

①对象的创建

<1>分配内存空间:堆区分配内存,并返回首地址。

<2>初始化:为对象的实例变量设置初始值【init将内存空间的数据清零】

Teacher *teacher = [[Teacher alloc] init];

指针(teacher)存储对象的首地址,代指对象,进行操作

4.instancetype和id区别

①instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象。

②instancetype只能作为返回值和参数,id还可以用来定义变量

5.实例变量

①实例变量变量可见度

@interface Child : NSObject
{
@public// 作用范围:外部、内部、继承
NSString *_name;
@protected// 【系统默认】作用范围:内部、继承
NSInteger _age;
@private// 作用范围:内部
CGFloat _score;
}
@end

②实例变量必须在花括号内

6.方法

①类中不能出现同名方法

②“:”标识参数,不能省略。有冒号必须有参数。

③冒号属于方法名的一部分

④“+”类方法

⑤“-”实例方法

// 方法名:printNum:
// 有参(1个参)无返回值
- (void)printNum:(NSInteger)num;
// 有两个参数
// 方法名:printName:age:
- (void)printName:(NSString *)name age:(NSInteger)age; //有三个参数
//方法名:printName:age:score:
- (void)printName:(NSString *)name age:(NSInteger)age score:(CGFloat)score;

7.在OC中使用消息发送机制:[receiver message]

表述:

①给received对象发送message消息

②received接收到消息,即方法message

③teacher找到message方法,并执行。

8.setter和getter方法

①setter(设置器)

<1>格式

- (void)setAge:(NSInteger)age;

即set+首字母大写的实例变量名(忽略下划线)

②getter(访问器)

<1>格式

- (NSInteger)age;

即返回值类型与变量类型一致,方法名与实例变量名相同(忽略下划线)

③无论setter还是getter内部操作的是实例变量

④每一个实例变量都需要一对setter和getter方法

⑤点语法只对setter和getter方法有效

9.@class的作用

只是表示有这么个类型,其他什么都没做。【注意:一般在.h中使用@class,在.m中引入头文件】

二、初始化方法

1.继承

①保证类的完整,简化代码

②NSObject是根类

③继承的内容:所有实例变量和方法

④继承是单向的,不能相互继承

⑤继承具有传递性

⑥子类可以重写父类的方法

2.super

①编译器指令,并非对象

②作用:给super发消息,可以执行父类该方法的实现

3.self

①系统关键字

②self在方法中指代当前方法的调用者

在实例方法中,指代调用当前方法的对象

在类方法中,指代当前类

4.初始化方法

①作用:为某些实例变量赋初值

②初始化方法在对象的整个生命周期里只使用一次【注:初始化方法是在对象的初始化阶段完成其实例变量的赋值操作,一个对象的初始化阶段只有一次,所以初始化方法只使用一次】

③“-”实例方法

- (instancetype)init{
// self在实例方法中代表实例对象
// self在类方法中代表类
self = [super init];
// 判断从父类继承过来的init方法是否初始化成功
if (self != nil) {//if(self) //if(self = [super init]) 非零即为真
// 初始化实例变量
_name = @"大白";
}
// 返回初始化完成的对象
return self;
}

<1>使用super调用父类的初始化方法,用于初始化继承自父类的公共实例变量
<2>初始化完成之后会返回一个地址,这个地址就是对象的地址
<3>self是一个指针,指向自己的对象。self保存返回的地址。再初始化自身特有变量。
<4>返回值有可能为空。如果返回值为空,就什么也不做。返回值不为空,初始化自己的实例变量。

5.指定初始化方法

①一个类可以有多个初始化方法

②虽然可以有多个初始化方法,但是一个对象只能使用一个初始化方法。

③通常会把在初始化时想做的操作全部放到指定初始化方法中

④选取原则:一般选参数最多的初始化方法作为指定初始化方法

⑤情况1:

.h中声明

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;

.m中实现

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age{

    if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}

⑥情况2:

.h声明

- (instancetype)initWithName:(NSString *)name;

- (instancetype)initWithName:(NSString *)name Age:(NSInteger)age;

- (instancetype)initWithName:(NSString *)name Age:(NSInteger)age Score:(CGFloat)score;

.m实现

#import "Student.h"
@implementation Student
- (instancetype)initWithName:(NSString *)name Age:(NSInteger)age Score:(CGFloat)score{ if (self = [super init]) {
_name = name;
_age = age;
_score = score;
}
return self;
} - (instancetype)initWithName:(NSString *)name Age:(NSInteger)age{
//凡是基本数据类型填0,对象、类填nil
return [self initWithName:name Age:age Score:];
} - (instancetype)initWithName:(NSString *)name{ return [self initWithName:name Age: Score:];
}
@end

6.便利构造器

①内部实现:封装了alloc和初始化操作,创建对象更加方便

②“+”类方法

③返回本类型的实例

④方法名以类名开头

⑤可以有0到多个参数

.声明

+ (instancetype)teacherWithName:(NSString *)name age:(NSInteger)age;

.实现

+ (instancetype)teacherWithName:(NSString *)name age:(NSInteger)age{

    Teacher *t = [[Teacher alloc]initWithName:name age:age];
return t;
}

.调用

Teacher *t2 = [Teacher teacherWithName:@"Lee" age:];

三、属性和点语法

1.属性

①提供setter、getter方法的默认实现

②关键字:

@property(声明)

@synthesize(实现)【可以省略,没有实现setter和getter方法时,通过该关键字自动生成】

③如果方法内部操作的实例变量未定义,系统会自动生成一个_属性名的实例变量,但是生成的实例变量的可见度是私有的,子类不可访问。

④一旦同时重写了setter、getter方法,并且没有实现@synthesize,@synthesize就不再生成实例变量。需要写@synthesize

2.属性特性

①读写性

readonly:只读状态(只生成getter方法)

readwrite:读写状态(setter和getter方法都生成)【系统默认】

setter=:指定属性生成的setter方法的名字

getter=:指定属性生成的getter方法的名字

②原子性

atomic:原子特性。setter、getter内部做了多线程处理。【系统默认】

nonatomic:非原子特性,生成普通setter、getter方法。【通常使用】

③语义特性

<1>MRC(手动引用计数)

assgin:非对象类型(比如int、float)属性的语义设置

@property (nonatomic, assign) NSInteger age;
setter⽅方法内部实现:
- (void)setAge:(NSInteger)age {
_age = age;
}
getter⽅方法内部实现:
- (NSInteger)age {
return _age;
}

retain:对象类型(比如:NSString,NSMutableDictionary等)属性的语义设置

@property (nonatomic, retain) NSString *name;
setter⽅方法内部实现:
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
getter⽅方法内部实现:
- (NSString *)name {
return [[_name retain] autorelease];
}

copy:对象类型并且想得到对象的副本(NSString)

@property (nonatomic, copy) NSString *gender;
setter内部实现
- (void)setGender:(NSString *)gender {
if (_gender != gender) {
[_gender release];
_gender = [gender copy];
}
}
getter内部实现
- (NSString *)gender {
return [[_gender retain] autorelease];
}

跟retain不同,一个对象想要copy,生成自己的副本,需要服从

<2>ARC(自动引用计数)

assign:修饰基本数据类型

weak:修饰类对象

strong:修饰类对象

copy:修饰类对象

四、OC字符串和数值

1.API文档

①Xcode -> Help ->Documentation and API Reference

Inherits form 继承关系

Conforms to 遵循什么协议

framework 属于哪个框架

Availability 什么时候可以用

Declared in 声明在什么头文件里

Related documents 相关文档

Sample code 示例代码

②option+鼠标左键

③command+鼠标左键

2.字符串(NSString和NSMutableString)

①OC中字符串由unichar(unicode)组成

②NSString字符串(不可变)

<1>创建

     // 创建字符串(错误)
// NSString是不可变字符串,意味着创建之后不能改变,使用init完成之后字符串就创建完了,只得到一个空字符串。
NSString *s2 = [[NSString alloc] init]; // 正确创建字符串
// 格式化初始字符串
NSString *s3 = [[NSString alloc] initWithFormat:@"Lanou%@",s1];
//便利构造器
NSString *s4 = [NSString stringWithFormat:@"Lanou%@",s1];
// 字面量
NSString *s5 = @"hello";

<2>常用方法

    NSString *str1 = @"abcdefg";
// 获取字符串长度
NSLog(@"%ld",[str1 length]); // 获取指定位置的字符(不推荐)
unichar c = [str1 characterAtIndex:]; // 判断字符串是否相等
BOOL b = [str1 isEqualToString:@"abcdefg"]; // 比较两个字符串大小
NSComparisonResult result = [str1 compare:@"bbb"]; // 截取子串
// 从哪开始截取
NSString *str2 = [str1 substringFromIndex:]; // 从开始到哪结束
NSString *str3 = [str1 substringToIndex:]; // 从哪开始截取,到哪里结束(获取单个字符使用这个方法)
// NSRange r = {0,3};
NSString *str4 = [str1 substringWithRange:NSMakeRange(, )]; // 拼接字符串
NSString *str5 = [str1 stringByAppendingFormat:@"hi"]; // 替换字符串
NSString *str6 = [str1 stringByReplacingCharactersInRange:NSMakeRange(, ) withString:@"xxx"]; // 字符串对象转化为int型
// 注意:不要在数字中加入字符,否则字符在哪就在哪停止
NSString *str7 = @"";
NSInteger i = [str7 intValue]; // int转为字符串
NSString *str1 = [NSString stringWithFormat:@"%d",number]; // 字符串全部大写
NSString *str8 = [str1 uppercaseString]; // 字符串全部小写
NSString *str9 = [str1 lowercaseString]; // 字符串首字母大写
NSString *str10 = [str1 capitalizedString]; // 判断后缀(重点)
// 后缀:以字符串最后一个字符结尾的子串
// 前缀:以首字母开头的子串 // 是否以指定字符串为后缀
BOOL b2 = [str1 hasSuffix:@"g"]; // 是否以指定字符串为前缀
BOOL b3 = [str1 hasPrefix:@"a"];

②NSMutableString(动态可变字符串)

<1>创建

Capacity参数值为预估的空间大小,但是会根据实际的存储情况,动态的调整实际空间大小【翻倍】

初始化方法
NSMutableString *strm1 = [[NSMutableString alloc] initWithCapacity:];

<2>常用方法

// 拼接字符串
[strm1 appendFormat:@""]; // 在哪里插入字符串
[strm1 insertString:@"" atIndex:]; // 删除字符串
[strm1 deleteCharactersInRange:NSMakeRange(, )]; // 替换字符串
[strm1 replaceCharactersInRange:NSMakeRange(, ) withString:@""]; // 重置字符串
[strm1 setString:@"abc"];

3.数值类(NSNumber)

①作用:实现基本数据类型与OC对象类型的相互转化

②基本数据类型(int,float等)转换为NSNumber

// int类型转化为NSNumber类型的对象
+ (NSNumber *)numberWithInt:(int)value;
// float类型转化为NSNumber类型的对象
+ (NSNumber *)numberWithFloat:(float)value;
// char类型转化为NSNumber类型的对象
+ (NSNumber *)numberWithChar:(char)value;

③NSNumber转换为基本数据类型(int,float等)

// NSNumber类型对象转化为int类型的数据
@property (readonly) int intValue;
// NSNumber类型对象转化为float类型的数据
@property (readonly) float floatValue;
// NSNumber类型对象转化为char类型的数据
@property (readonly) char charValue;

④常用方法

// NSNumber类型对象的⽐比较
- (NSComparisonResult)compare:(NSNumber *)otherNumber;

⑤字面量

// 常量:
NSNumber *intNumber = @;
NSNumber *charNumber = @‘w';
// 变量:
int age = ;
NSNumber *ageNumber = @(age);
char gender = 'w';
NSNumber *genderNumber = @(gender);

4.NSValue(完成结构体和对象类型的转换)

①结构体(NSRange等)转换为NSValue

②NSValue转换为结构体(NSRange等)

③常用方法

// NSRange类型转化为NSValue类型的对象
+ (NSValue *)valueWithRange:(NSRange)range;
// NSValue类型转化为NSRange类型的结构体变量
@property (readonly) NSRange rangeValue;

五、集合

1.数组类(NSArray和NSMutableArray)

①只能存储对象类型,但是对于对象的类型没有限制【有序的集合】

②NSArray(不可变数组)

<1>创建

// 注意:nil作为数组的结束标志,不要手动添加。
// 初始化方法
NSArray *arr1 = [[NSArray alloc] initWithObjects:@"a",@"b",@"c", nil];
// 便利构造器
NSArray *arr2 = [NSArray arrayWithObjects:@"d",@"e",@"f",@"g",nil];
// 字面量(结尾不需要加nil)
NSArray *arr3 = @[@"mike",@"Joe",@"kitty"];

<2>常用方法

// 获取元素个数
NSUInteger c1 = arr1.count;//[arr1 count] // 获取数组中指定下标对应的元素
NSString *obj1 = [arr3 objectAtIndex:]; // 字面量获取元素
NSLog(@"%@",arr3[]); // 判断数组中是否包含某个元素
BOOL b1 = [arr1 containsObject:@"m"]; // 返回一个元素在数组中的索引
NSUInteger n1 = [arr1 indexOfObject:@"b"]; // 分割字符串
NSString *str1 = @"www.lanou3g.com";
NSArray *arr4 = [str1 componentsSeparatedByString:@"."]; // 拼接字符串
NSString *str2 = [arr4 componentsJoinedByString:@"/"];
NSLog(@"%@",str2);

③NSMutableArray(可变数组)

<1>创建

// 初始化方法
NSMutableArray*marr1 = [[NSMutableArray alloc]initWithCapacity:]; // 便利构造器
NSMutableArray *marry2 = [NSMutableArray arrayWithCapacity:]; // 可变数组使用字面量(字面量创建的数组是不可变的)
NSMutableArray *marr3 =@[@"a",@"b",@"c"].mutableCopy
//[@[@"a",@"b",@"c"] mutableCopy]点语法getter

<2>常用方法

// 添加元素
[marr1 addObject:@"a"]; // 插入元素
[marr1 insertObject:@"x" atIndex:]; // 删除元素
//按元素删
[marr1 removeObject:@"x"];
//按位置删
[marr1 removeObjectAtIndex:];
// 删除最后一个
[marr1 removeLastObject];
// 删除全部
[marr1 removeAllObjects]; // 使用指定元素替换指定位置上的元素
[marr4 replaceObjectAtIndex:marr4.count - withObject:@"e"]; // 交换两个指定位置的元素
[marr4 exchangeObjectAtIndex: withObjectAtIndex:];

2.字典类(NSDictionary和NSMutableDictionary)

①用来存储一一对应关系的数据【无序的集合】

②key和value必须是对象类型,每一对称为一个条目

③靠key存取元素

④NSDictionary(不可变字典)

<1>创建

// 初始化方法
NSDictionary*dict1 = [[NSDictionaryalloc]initWithObjectsAndKeys:@"Mike",@"M",@"Lee",@"L",@"Kitty",@"K",nil]; // 字面量
NSDictionary *dict2 = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};

<2>常用方法

// 获取键值对个数
NSUInteger c1 = dict1.count;//[dict1 count] // 获取字典中所有的key值
NSArray *arr1 = dict1.allKeys;//[dict1 allKeys] // 获取字典中所有的value
NSArray *arr2 = dict1.allValues;//[dict1 allValues] // 根据key获得对应的value
NSString *obj1 = [dict1 objectForKey:@"M"]; // 字面量获取
NSString *obj2 = dict1[@"L"]; //注意:在字典中key不能重复
NSDictionary *dict3 =@{@"M":@"Mike",@"L":@"Lee",@"T":@"Tim",@"L":@"Lucy"};

⑤NSMutableDictionary(不可变数组)

<1>创建

// 初始化方法
NSMutableDictionary *mdict1 = [[NSMutableDictionary alloc] initWithCapacity:]; // 字面量
NSMutableDictionary *mdict2 = @{@"M":@"Mike",@"L":@"Lee"}.mutableCopy;

<2>常用方法

// 添加(如果字典中没有给定的key,就添加这个键值对)
[mdict2 setValue:@"Tim" forKey:@"T"]; // 修改(如果字典中有给定的key,直接修改value)
[mdict2 setObject:@"Lucy" forKey:@"L"]; // 给定key删除
[mdict2 removeObjectForKey:@"M"]; // 删除所有键值对
[mdict2 removeAllObjects];

3.集合类(NSSet和NSMutableSet)

①互异性、无序性,经常用来处理重用问题

②NSSet(不可变集合)

<1>创建

// 初始化⽅方法
NSSet *name = [[NSSet alloc] initWithObjects:@"frank", @"duck", @"monkey",nil]; // 便利构造器
NSSet *name = [NSSet setWithObjects:@"frank",@"duck", @"monkey", nil];

<2>常用方法

// 元素个数
NSLog(@"%ld",set.count); // 将set中的所有元素放到数组中
NSArray *setArr = set.allObjects; // 任意取出一个元素
NSString *s1 = [set anyObject]; // 判断set中是否包含给定对象
BOOL b = [set containsObject:@""];

③NSMutableSet(可变集合)

<1>创建

// 初始化⽅方法
NSMutableSet *name = [[NSMutableSet alloc] initWithCapacity:]; // 便利构造器
NSMutableSet *name = [NSMutableSet setWithCapacity:];

<2>常用方法

// 添加⼀一个对象
- (void)addObject:(id)object;
// 移除⼀一个对象
- (void)removeObject:(id)object;
// 移除所有对象
- (void)removeAllObjects;

六、集合遍历和数组排序

1.for循环遍历

①原理:通过for循环的循环变量用作数组元素下标来获取不同下标的元素

②循环次数就是数组元素的个数

<1>遍历数组

    NSArray *arr = @[@"Mike",@"Lee",@"Kitty"];
for (int i = ; i < arr.count ; i ++) {
NSLog(@"%@",arr[i]);
}

<2>遍历字典

    NSDictionary *dict = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};
// 获取key
NSArray *keyArr = dict.allKeys;
// 遍历数组
for (int i = ; i < keyArr.count; i ++) {
NSLog(@"%@",dict[keyArr[i]]);//[dict objectForKey:keyArr[i]]
}

<3>遍历集

    NSSet *set = [NSSet setWithObjects:@"",@"",@"", nil];
// 将集合中的元素放到数组中
NSArray *allArr = set.allObjects;
for (int i = ; i < allArr.count; i ++) {
NSLog(@"%@",allArr[i]);
}

2.枚举器(NSEnumeration)

①遍历集合中的元素

②依附于集合类,没有用来创建实例的接口

③对可变集合进行枚举操作,不能通过添加或删除对象这类方式改变集合容器的元素个数

④注意:由于字典和集合中存储的元素是无序的,因此没有反向枚举的概念

<1>数组

    NSArray *arr2 = @[@"",@"",@""];
// 创建枚举器(必须依靠一个容器对象)
// 正向
NSEnumerator *enum1 = [arr2 objectEnumerator];
// 创建一个保存取出元素的变量
id value1 = nil;
// 循环取值
while (value1 = [enum1 nextObject]) {
NSLog(@"%@",value1);
} // 逆向
NSEnumerator *enum2 = arr2.reverseObjectEnumerator;
// 创建一个保存取出元素的变量
id value2 = nil;
// 循环取值
while (value2 = [enum2 nextObject]) {
NSLog(@"%@",value2);
}

<2>字典

    NSDictionary *dict2 = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};
// 创建枚举器
NSEnumerator *enum3 = [dict2 objectEnumerator];
// 创建一个保存取出元素的变量
id value3 = nil;
// 循环变量
while (value3 = [enum3 nextObject]) {
NSLog(@"%@",value3);
}

<3>集

    NSSet *set2 = [NSSet setWithObjects:@"",@"",@"", nil];
// 创建枚举器
NSEnumerator *enum4 = [set2 objectEnumerator];
// 创建一个保存取出元素的变量
id value4 = nil;
// 循环变量
while (value4 = [enum4 nextObject]) {
NSLog(@"%@",value4);
}

3.for...in

①对可变集合进行快速枚举操作时,不能通过添加或删除对象这类方式来改变集合容器的元素个数

<1>数组

    NSArray *arr3 = @[@"",@"",@""];
for (NSString *str in arr3) {
NSLog(@"%@",str);
}

<2>字典

    NSDictionary *dict3 = @{@"M":@"Mike",@"L":@"Lee",@"K":@"Kitty"};
// 取出key
for (NSString *s in dict3) {
NSLog(@"%@",s);
}
// 取出value
for (NSString *s1 in dict3) {
NSLog(@"%@",dict3[s1]);
}

<3>集

    NSSet *set3 = [NSSet setWithObjects:@"",@"",@"", nil];
for (NSString *s1 in set3) {
NSLog(@"%@",s1);
}

4.数组排序

①数组是有序容器,因此集合中只有数组才能排序

②NSSortDescriptor

// 初始化方法
- (instancetype)initWithKey:(NSString *)key ascending:(BOOL)ascending;
// 数组根据排序条件进⾏行排序,得到排好序的新的数组对象。
- (NSArray*)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors;
sortDescriptors:数组类型的对象,数组中可以存放多个排序描述对 象,最终能够实现按照多个要求进⾏行排序的⽬目的。
⽐如:如果该参数中存储了由name和age两个Person属性参与创建 的NSSortDescriptor对象,在按name进⾏行排序的同时,如果遇到姓名相同的就会按照age进⾏行排序。
<1>数组中存放自定义类的对象
// 创建数组对象
NSArray *array = @[@"zhegnzhou", @"beijing",@"shanghai", @"guangzhou", @"xian", @"dalian"];
// 创建排序条件
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"self" ascending:YES];
// 数组根据排序条件进⾏行排序
NSArray *resultArray = [array sortedArrayUsingDescriptors:@[descriptor]];

<2>数组中存放直接可进行排序的对象

// 创建数组对象,数组中存储多个Person对象
NSArray *array = @[per1, per2, per3, per4, per5];
// 创建排序条件,按照Person对象的姓名降序排序
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
// 数组根据排序条件进⾏行排序
NSArray *resultArray = [array sortedArrayUsingDescriptors:@[descriptor]];

③其他数组排序方法

<1>不可变数组排序(排序结果生成新数组,原数组无改变)

 - (NSArray *)sortedArrayUsingSelector:(SEL)comparator;
// 注:SEL类型的参数comparator:需要传入一个返回结果是 NSComparisonResult的方法名。
NSArray *array = @[@"lanou", @"zhengzhou", @“henan",@“huimian"];
NSArray *newArray = [array sortedArrayUsingSelector:@selector(compare:)];

数组array中的元素都是字符串。字符串比较的方法compare:返回值类型正好满足SEL参数的需求

<2>可变数组排序(直接对原数组进行操作,无新数组生成)

     (void)sortUsingSelector:(SEL)comparator;
// 注:SEL类型的参数comparator:需要传入一个返回结果是 NSComparisonResult的函数

<3>数组中存放自定义的类的对象

NSArray *personArray = @[p1,p2,p3,p4,p5];

// 按姓名升序
NSArray *newNameArrayAscending = [personArray sortedArrayUsingSelector:@selector(compareByNameAscending:)]; // 按姓名降序
NSArray *newNameArrayDescending = [personArray sortedArrayUsingSelector:@selector(compareByNameDescending:)];
比较方法的声明
// 按姓名升序
- (NSComparisonResult)compareByNameAscending:(Person *)anotherPerson; // 按姓名降序
- (NSComparisonResult)compareByNameDescending:(Person *)anotherPerson;
比较方法的实现
// 按姓名升序
- (NSComparisonResult)compareByNameAscending:(Person *)anotherPerson{ return [self.name compare:anotherPerson.name]; } // 按姓名降序
- (NSComparisonResult)compareByNameDescending:(Person *)anotherPerson{ if (self.name < self.name) { return ; }else{ return ; } }

七、内存管理

1.OC内存管理的机制(GC和RC)

①GC:垃圾回收机制(Garbage-Collection)

程序员只需开辟内存空间,不需要用代码的形式释放,系统来判断哪些空间不再被使用,并回收这些内存空间,以便再次分配。整个回收的过程不需要写任何代码,由系统自动完成垃圾回收。Java开发中一直使用的就是垃圾回收技术。

②RC:引用计数机制(MRC和ARC)【iOS支持的两种内存管理方式】

<1>MRC(Manual Reference Counting)人工引用计数【内存管理机制:引用计数】

内存的开辟和释放都由程序代码进行控制。相对垃圾回收来说,对内存的控制更加灵活,可以在需要释放的时候及时释放。

<2>ARC(Auto Reference Counting)自动引用计数【基于MRC】

iOS 5.0的编译器特性,它允许用户开辟空间,不用去释放空间。它不是垃圾回收!它的本质还是MRC,只是编译器帮程序员默认加了释放的代码。

2.引用计数机制【栈结构的先进后出】

①每个对象都有一个引用计数器,用来记录当前对象的引用次数

②当一个新的引用指向对象时,引用计数器就加1,当去掉一个引用时,引用计数就减1.当引用计数到零时,该对象的空间就被系统回收。

③retainCount获取对象的引用计数
④方法:

<1>+alloc(生成对象)【分配内存并且将内存的引用计数置为1】

<2>-retain(持有对象)【引用计数加1】【通常来说,一块内存有几个指针指向,引用计数就应该是几】

<3>-copy【把某一对象的内容拷贝一份,拷贝出新的对象,原有对象的引用计数不变,新的对象的引用计数变1】

<4>-release【引用计数立即减1】/-autorelease【未来的某一时刻引用计数减1】(释放对象)

【-release:一个指针不再使用这块内存,就应该release。release完成之后,最好指针置为nil(空)】

p1 = nil;// 改变P1指向

【-autorelease:通过autoreleasepool自动释放池,控制autorelease对象的释放】

<5>-dealloc(销毁对象)

继承自父类的方法,当对象引用计数为0的时候,由对象自动调用,销毁该对象的空间;

// 当对象释放前会执行该方法
- (void)dealloc{
NSLog(@"释放%@",_name);
// 调用父类中的dealloc
[super dealloc];// 父类对该方法的实现才是真正的回收空间
}

⑤NSAutoreleasePool(自动释放池)

<1>方式一:

// 创建自动释放池
NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];
代码内容;
// 自动释放池销毁
[pool1 release];

<2>方式二:【自动释放池使用这种,更安全】

@autoreleasepool { // 创建自动释放池} // 自动释放池销毁

3.内存管理原则

①在一段代码内,增加和减少的次数要相等

②如果增加的次数大于减少的次数,会造成内存泄露

③如果增加的次数小于减少的次数,会造成内存过度释放

④如果增加的次数等于减少的次数,还继续访问,造成野指针问题

4.协议(Protocol)【一个类可以签多个协议,用逗号隔开<,>】

①只有.h文件

②接受协议的类实现协议中定义的方法

③实现方法:command+n -> 选择os x下的source -> Objective-C File -> Next -> File Type(选择Protocol)

④结构

@protocol MyProtocol <NSObject>
//【NSObject可以选择性删除,继承于NSObject】
@required// 方法必须实现(默认)
- (void)eating;
@optional// 可选实现的
- (void)sayHi;
@end

5.拷贝

①copy方法

<1>跟retain不同,一个对象想要copy,生成自己的副本,需要服从NSCopying协议,定义copy的细节(如何copy)。如果没有接受NSCopying协议而给对象发送copy消息,会引起crash

<2>copy方法的实现

  <NSCopying>

<3>copy方法的使用

- (id)copyWithZone:(nullable NSZone *)zone{
//伪拷贝:相当于retain【拷贝地址,引用计数加1】
return [self retain]; // 浅拷贝:只对对象进行开辟新空间,对象中的属性公用
//【对象开辟新的空间,但是两个对象的实例变量指向同一块空间】
Person *temp = [[Person allocWithZone:zone] init];
temp.name = self.name;
return temp; // 深拷贝:对对象开辟空间,对象中属性也开辟空间,然后内容全部复制过去
//【对象开辟新的空间,两个对象的实例变量也指向不同的空间】
Person *temp = [[Person allocWithZone:zone] init];
temp.name = [[NSString alloc] initWithString:self.name];
return temp;
}

<4>【不是任何对象都可以接收copy消息,只有接受了NSCopying协议的对象才能接收copy消息】

八、高级内存管理

1.属性的语义特性(setter和getter方法优化)

①assign(使用范围:基本数据类型:char,short,int,float,double)

     @property (nonatomic,assign)NSInteger name;
// ①setter
- (void)setName:(NSString *)name{
_name = name;
}
// ②getter
- (NSString *)name{
return _name;
}

②reetain(使用范围:对象类型)

     @property (nonatomic,retain)NSString *name;
// ①setter
- (void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
// ②getter
- (NSString *)name{
// 防止野指针
return [[_name retain] autorelease];
}

③copy(对象类型,且遵守了<NSCopying>协议)

【如果要对一个对象进行copy,那该对象所属的类必须遵守<NSCopying>协议】

     @property (nonatomic,copy)NSString *name;
// ①setter
- (void)setName:(NSString *)name{ if (_name != name) {
[_name release];
_name = [name copy];
}
}
// ②getter
- (NSString *)name{
// 防止野指针
return [[_name retain] autorelease];
}

2.dealloc释放实例变量

①dealloc是NSObject的一个实例方法,用于回收alloc开辟的内存空间
②在对象引用计数为0是,由系统自动调用

③通常在dealloc中释放类的实例变量

// 重写销毁
- (void)dealloc{
// 在对象销毁之前,将对象中的实例变量和属性销毁
[_name release];
[super dealloc];
}

④注意:

<1>永远不要手动调用dealloc

<2>在dealloc方法的最后一行,必须要写[super dealloc],让系统真正的去销毁对象

3.初始化方法【优化】

// 初始化方法
- (instancetype)initWithName:(NSString *)name{
if (self = [super init]) {
// 调用setter方法
// 以后所有方法中给实例方法赋值,都使用setter方法【切记,很重要】
self.name = name;// 如果不用,可能出现野指针问题
}
return self;
}

4.便利构造器的内存管理

①便利构造器,一定配套使用自动释放池

②所有使用便利构造器创建的对象都不需要释放

+ (instancetype)personWithName:(NSString *)name{

    Person *p =  [[Person alloc] initWithName:name];
return [p autorelease];
}

5.集合的内存管理

①常见的集合类有:NSArray,NSDictionary,NSSet
②集合会自主管理集合内部元素
③流程
<1>加入集合的元素会被retain
<2>移除集合的元素会被release
<3>集合被释放时,会对集合中所有元素release
6.KVC
①KVC:key Value Coding ,键值编码,是一种间接访问实例变量的方法。
②KVC提供了一种使用字符串(key)而不是访问器方法,去访问一个对象实例变量的机制
③KVC按顺序使用如下技术:
<1>检查是否存在getter方法-<key>或者setter方法-set<key>:的方法
<2>如果没有上述方法,则检查时候存在名字为-_<key>、<key>的实例变量
<3>如果仍未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,可以根据需要重写。
④常用方法
<1>
取值
- (id)valueForKey:(NSString *)key;
设置值
- (void)setValue:(id)value forKey:(NSString *)key;
以Person类为例
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end // 属性
Person *p = [[Person alloc] init]; p.name = @"张三";
NSLog(@"%@", p.name); // KVC
Person *p = [[Person alloc] init];
[p setValue:@"张三" forKey:@"name"]; NSLog(@"%@", [p valueForKey:@"name"]);
<2>
获取键值路径
- (id)valueForKeyPath:(NSString *)keyPath;
设置键值路径
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
以Person类为例
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *gender;
@end // 属性
Person *p = [[Person alloc] init]; p.name = @"张三";
p.gender = @"男";
NSLog(@"%@ %@", p.name, p.gender); // KVC
Person *p = [[Person alloc] init];
NSDictionary *dic = @[@"name": @"张三", @"gender": @"男"];
[p setValuesForKeysWithDictionary:dic]; NSLog(@"%@ %@", [p valueForKey:@"name"], [p valueForKey:@"gender"]);
<3>
模型类:写的属性必须和字典中key值是一样的【一模一样】
- (void)setValuesForKeysWithDictionary:(NSDictionary*)keyedValues;
 Student.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>tel</key>
<string></string>
<key>name</key>
<string>Mike</string>
<key>age</key>
<integer></integer>
<key>sex</key>
<string>男</string>
</dict>
<dict>
<key>tel</key>
<string></string>
<key>name</key>
<string>Joe</string>
<key>age</key>
<integer></integer>
<key>sex</key>
<string>男</string>
</dict>
<dict>
<key>tel</key>
<string></string>
<key>name</key>
<string>Kitty</string>
<key>age</key>
<integer></integer>
<key>sex</key>
<string>女</string>
</dict>
</array>
</plist>
 Student.m

 #import "Student.h"
@implementation Student
// 如果使用KVC赋值,一定重写这个方法。这个方法可以什么都不写。作用防止找不到对应的key而crash【崩溃】
// 【在解析有大用途】
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{ // NSLog(@"++=%@ : %@",key,value); }
@end
 Student.h

 #import <Foundation/Foundation.h>
// 模型类
@interface Student : NSObject
@property (nonatomic,copy)NSString *name;
@property (nonatomic,retain)NSNumber *age;
@property (nonatomic,copy)NSString *sex;
@end
 main.m

 #import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc, const char * argv[]) { NSArray *datArray = [NSArray arrayWithContentsOfFile:@"/Users/lanou3g/Desktop/OClesson8练习/OClesson8练习/Student.plist"];
// 容器(保存对象的数组)
NSMutableArray *arr = [NSMutableArray array];
for (NSDictionary *dict in datArray) {
Student *s = [[Student alloc] init];
[s setValuesForKeysWithDictionary:dict];
[arr addObject:s];
}
for (Student *stu in arr) {
NSLog(@"%@ : %@ : %@",stu.name,stu.age,stu.sex);
} return ;
}

⑤当使用KVC时,如果KVC值和属性名不一样时,在类中重写以下两个方法:

【在使用KVC赋值时,一定要重写这个方法,这个方法可以什么都不写。作用防止找不到对应的key而崩溃】

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
}
- (id)valueForUndefinedKey:(NSString *)key {
}

7.ARC

①ARC:Automatic Reference Counting,自动引用计数,由开发人员开辟内存空间,但是不需要释放该内存空间,由系统自动释放该空间
②ARC本质上还是基于MRC的,只不过是系统自动添加了释放内存的方法
③ARC是编译器特性
④从Xcode5.0后,创建的工程默认是开启ARC的【系统默认ARC】
⑤当工程开启ARC后,由于编译器会自动帮你释放内存,所有和内存相关操作retain、release、autorelease,都不能写。【copy可以用】
⑥重写dealloc方法时,也不能写[super dealloc]
⑦属性语义特性
<1>assign:基本数据类型(char,short,int,float,double)
<2>strong:对象类型,相当于MRC中的retain
<3>copy:对象类型,且遵守了<NSCopying>协议
<4>weak:对象类型,但是内部不会对对象做retain操作
⑧ARC和MRC混编
如果需要对特定文件开启或关闭ARC,可以在工程选项中选择:
Targets -> Compile Phases ->Compile Spurces
在里面找到对应文件,添加flat
<1>打开ARC:-fobjc-arc
<2>关闭ARC:-fno-objc-arc
 
九、类的扩展
1.对比:
①子类化:创建一个子类直接继承自原有类,在该类中扩充新的功能,该方式既可以扩充方法,也可以扩充实例变量。但是,想使用扩充的功能,必须使用子类的对象,原有类的对象无扩充功能。
②修改源代码:这是一种最直接的添加功能的方式,但是局限于必须要拥有该类的源代码,我们才有修改的权限。该方式既可以扩充方法,也可以扩充实例变量。
③协议:这是一种间接扩充功能的方式,协议中只有一堆方法的声明,使用时需要类服从协议,实现协议中的方法来扩充功能,而且只能扩充方法,不能扩充实例变量。局限于必须要拥有该类的源代码。
2.Category(分类、类目)
①也叫分类,类目。是为没有源代码的类扩充功能
②扩充的功能会成为原有类的一部分,可以通过原有类或者原有类的对象直接调用,并且可继承
③该方法只能扩充方法,不能扩充实例变量
④创建方式
<1>command +n 选择Objective-C File
<2>在File Type中选择文件类型:Category
<3>文件扩展类型里选择要写类目的类名
⑤使用:
<>在类目的.h文件中声明需要添加的方法
[]为没有源码的类添加方法【不能添加实例变量】
[]可以将类的方法按功能分类【添加的方法名不要和已有的方法名重名】
@interface NSString (AddMethod) //声明sayHi⽅方法 - (void)sayHi;
@end
<>在类目的.m文件中实现对应的方法
@implementation NSString (AddMethod) //实现sayHi⽅方法
- (void)sayHi {
NSLog(@"我是⽜牛逼的字符串"); }
@end
<>min中使用
[]使用类目添加的方法首先需要在对应的类中导入类目的.h文件
#import <Foundation/Foundation.h>
#import "NSString+AddMethod.h"
int main(int argc, const char * argv[]) {
NSString *str = [[NSString alloc] init];
[str sayHi];
return ;
}

3.Extension(延展)

①私有变量(方法):类外部不能直接访问,甚至其子类也不能够直接访问
②使用场景:
<1>为能够获得源代码的类添加私有的实例变量和方法
<2>注意:延展操作的类必须是能够获得源代码的类(具有.m文件的类)
③通过延展定义的方法属于私有方法,外界是没有访问权限的,只能在当前类的.m文件中访问。
④格式:把代码写到原始类的.m中
⑤Teacher类中.m文件中的代码
<1>延展:为一个已有源代码的类添加私有实例变量和管理私有方法声明
@interface Teacher()
{
NSInteger _salary; //存储对应的薪资
}
- (NSInteger)getSalary; //声明⼀一个领⼯工资的⽅方
@end @implementation Teacher
//实现领⼯工资的操作
- (NSInteger)getSalary {
NSLog(@"这个⽉月发了不少,可以吃⼤大餐了"); _salary = ; return _salary; }
@end

4.delegate(代理)设计模式

①要素:委托方,代理方,协议
<1>委托方:委托别人去执行某些操作的人(对象)
<2>协议(Protocol):委托方需要代理方执行的操作
<3>代理方:被委托去执行某些操作的人(对象)
②协议(Protocol)是默认实现的,即@required;@optional修饰的方法是可选的,可实现也可不实现
③设计步骤:
<1>委托方指定一套协议(在委托方的.h文件中),协议中声明委托方需要让代理方执行的方法(只有方法声明)
<2>委托方声明一个delegate属性(assign修饰),存储代理方对象
<3>代理方需要遵守协议,并且对协议中的方法进行实现
<4>将代理方设置为委托方的代理人(将代理方对象赋值给委托方对象的delegate属性,进行存储)
<5>委托方在合适的时机通知代理方对象去执行相应的操作
④respondsToSelector:该方法判断对象是否能够对某一个选定的方法做出响应。
①Girl.h
#import <Foundation/Foundation.h> // 协议 @protocol MyProtocol <NSObject> // 触发了某些事情 // 吃东西
- (void)eating; // 喝东西
- (void)drinking; // 逛街
- (void)shopping; @end // <MyProtocol> @interface Girl : NSObject @property (nonatomic,copy)NSString *name; // 准备代理属性(weak弱引用,有关系但不持有)【必须使用weak】 // 代理必须标明协议 @property (nonatomic,weak)id<MyProtocol> delegate; // 女孩饿了
- (void)hungry; // 女孩渴了
- (void)thirsty; // 女孩不开心了
- (void)unHappy;
@end //Girl
②Girl.m
#import "Girl.h" @implementation Girl // 女孩饿了
- (void)hungry{
// 代理调用吃东西的方法
// 使用代理需要做判断(是否有代理和是否有该方法)
if (_delegate != nil && [_delegate respondsToSelector:@selector(eating)]) {
[_delegate eating];
}else{
NSLog(@"自己做饭吃");
}
} // 女孩渴了
- (void)thirsty{
// 代理调用喝东西的方法
if (_delegate != nil && [_delegate respondsToSelector:@selector(drinking)]) {
[_delegate drinking];
}else{
NSLog(@"自己喝水");
}
} // 女孩不开心了
- (void)unHappy{
if (_delegate != nil && [_delegate respondsToSelector:@selector(shopping)]) {
[_delegate shopping];
}else{
NSLog(@"自己逗自己开心");
}
}
@end ③Boy.h
#import <Foundation/Foundation.h>
#import "Girl.h" @interface Boy : NSObject<MyProtocol> @end// Boy ④Boy.m
#import "Boy.h" @implementation Boy - (void)eating{
NSLog(@"男孩去买吃的");
} - (void)drinking{
NSLog(@"男孩去买水");
} - (void)shopping{
NSLog(@"男孩陪女孩逛街");
} @end// Boy ⑤main.m
#import <Foundation/Foundation.h>
#import "Girl.h"
#import "Boy.h" int main(int argc, const char * argv[]) {
Girl *g = [[Girl alloc] init];
Boy *b = [[Boy alloc] init]; // 设置代理
g.delegate = b; [g hungry];
[g thirsty];
[g unHappy];
return ;
}

OC学习心得【适合初学者】的更多相关文章

  1. windows类书的学习心得(转载)

    原文网址:http://www.blogjava.net/sound/archive/2008/08/21/40499.html 现在的计算机图书发展的可真快,很久没去书店,昨日去了一下,真是感叹万千 ...

  2. windows类书的学习心得

    原文网址:http://www.blogjava.net/sound/archive/2008/08/21/40499.html 现在的计算机图书发展的可真快,很久没去书店,昨日去了一下,真是感叹万千 ...

  3. 我的MYSQL学习心得(四) 数据类型

    我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(五) 运 ...

  4. 我的MYSQL学习心得(九) 索引

    我的MYSQL学习心得(九) 索引 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...

  5. 我的MYSQL学习心得(十) 自定义存储过程和函数

    我的MYSQL学习心得(十) 自定义存储过程和函数 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心 ...

  6. 我的MYSQL学习心得(十四) 备份和恢复

    我的MYSQL学习心得(十四) 备份和恢复 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) ...

  7. 我的MYSQL学习心得(十六) 优化

    我的MYSQL学习心得(十六) 优化 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  8. effective java 学习心得

    目的 记录一下最主要学习心得,不然凭我这种辣鸡记忆力分分钟就忘记白看了... 用静态工厂方法代替构造器的最主要好处 1.不必每次都创建新的对象 Boolean.valueOf Long.valueOf ...

  9. 推荐10个适合初学者的 HTML5 入门教程

    HTML5 作为下一代网站开发技术,无论你是一个 Web 开发人员或者想探索新的平台的游戏开发者,都值得去研究.借助尖端功能,技术和 API,HTML5 允许你创建响应性.创新性.互动性以及令人惊叹的 ...

随机推荐

  1. WF4 持久化 <第四篇>

    一.基础示例 WF4 默认支持SQLServer的持续化,首先要执行目录C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en下的脚本: SqlPer ...

  2. php xml 互相转换

    正好昨天才做过类似的需求……几行代码就可以搞定. 如果你使用 curl 获取的 xml data$xml = simplexml_load_string($data);$data['tk'] = js ...

  3. Sublime 3114 + 转换GBK方法

    下载地址: http://files.cnblogs.com/files/wuyifu/Sublime_Text_Build_3114_x64_Setup.zip -– BEGIN LICENSE - ...

  4. select 嵌套查询

    1. SELECT语句的子查询 语法:     SELECT ... FROM (subquery) AS name ... 先创建一个表: CREATE TABLE t1 (s1 INT, s2 C ...

  5. 关于原生js的一些研究

    搬砖,原文地址:http://segmentfault.com/a/1190000002911253 callee和caller function inner(){ console.log(argum ...

  6. thinkphp实现分页

    public function zhanshi(){ $cate = M('cate'); //实例化数据表 $count = $cate->count(); //获取总的记录数 $Page = ...

  7. Android IOS WebRTC 音视频开发总结(六十)-- 您为什么招不到适合的音视频人才

    本文主要介绍音视频行业招聘现状,文章最早发表在我们的微信公众号上,详见这里,欢迎关注微信公众号blackerteam,更多详见www.blackerteam.com 有过音视频人才招聘经验的应该都深有 ...

  8. Windows 7/8/8.1 硬盘安装法实现 ubuntu 14.04 双系统

    一.软件准备 1. 下载 Ubuntu 系统镜像:http://www.ubuntu.com/download/desktop/ : 这里使用的是 ubuntu 14.04.1 LTS 64bit 版 ...

  9. js随机生成字母数字组合的字符串 随机动画数字

    效果描述: 附件中只有一个index.html文件有效 其中包含css以及html两部分内容 纯js生成的几个随机数字 每次都不重复,点击按钮后再次切换 使用方法: 1.将css样式引入到你的网页中 ...

  10. 如何解决谷歌浏览器下jquery无法获取图片的尺寸

    代码如下: $(document).ready(function(){ var img_h=$img.height(); var img_w=$img.width(); }) 以上代码在IE和火狐中没 ...