12.14
1.提纲:
a.面向对象与面向过程
b.类和对象
c.设计类
d.OC简介
e.类和对象的使用
2.面向对象与面向过程
面向对象、面向过程是思考问题的两种不同的方式:
a.面向过程 注重 业务功能
b.面向对象 注重 封装和层次
3.面向对象的三个基本特征:封装、继承、多态
4.类和对象:
类 相当于结构体
对象 相当于结构体中的类型
类是对无数个对象的属性和行为的描述。
开发一个程序,首先得有对象,想要创建对象,那么必须得有类。
5.面向对象开发过程:
a.分析应用需要哪些类
b.设计类
c.创建对象并使用对象
6.一般名词都是类,具有相同(或者类似)属性和行为的对象都可以抽象出一个类。
7.设计类
a.确定类的类名
b.确定类具有的属性
c.确定类的行为(具有的功能)
8.OC中设计类名的规范:类名的第一个字母必须大写、不要有下划线(驼峰标识)。
9.OC的特点:
兼容性:是一种面向对象的C语言,可以有C和C++语句,可以调用C的函数,也可以通过C++对象访问的方法。
类:定义类是基本能力,OC的类声明和实现,包括接口部分和实现部分。定义类要求前缀大写,首字母大写。
方法:定义方法也是它的基本能力,OC中的方法是通过[]运算符进行调用,有时也称作“消息发送”。
属性:是OC2.0提出的概念,代替对成员变量访问的“读取方法(getter)”和“设置方法(setter)”的手段。
协议:OC中来的协议类似于Java中接口或C++的纯虚类,只有接口部分定义没有实现部分。
分类:OC中的分类类似于继承机制,通过分类能够扩展父类的功能。
10. OC的缺点:
a.OC的最初版本并不支持垃圾回收。
b.OC不包括民命名空间机制。
c.OC不支持运算符重载。
d.OC不支持多继承。
故,OC不是一个完全面向对象语言。
11. OC的扩展名:
a.在Mac OS X下使用Xcode进行开发。
b. Xcode通过.m扩展名表示文件使用的时OC代码(可以嵌入C语言)。
c.如果需要嵌入C++的代码,则扩展名应为.mm。
12.OC中使用#import代替#include来包含头文件
#import能够自动防止头文件被重复包含,#include则需要添加那三句条件编译。Foundation.h是Foundation框架的头文件,Foundation框架提供了OC开发中一些列基本的类及函数.
13.NSLOG()是OC中的输出语句,和printf功能类似,NSLOG会自动添加\n。NSLOG()使用日志格式的输出,会有一个较长的前缀。@“”是OC中的字符串表示形式,在OC中,@表示对象,而且很多OC中的关键字都是以@开头。NSLOG()如果想要打印OC对象的值,使用%@。
14.其他的基本说明:
a.布尔类型:布尔类型用于描述“真”、“假”,类型为BOOL,其值可为YES NO。
b.OC对象指针类型:
void * 任意类型的指针。
id OC的对象指针。
instancetype 和id类似,用于构造方法的返回值类型。
c.OC中的“空”:
NULL 对应普通指针变量的地址。
nil 表示OC对象指针的空地址。
Nil 表示OC类对象指针的空地址。
15.接口与实现的分离
OC定义一个类,分为两部分:接口、实现。
接口(公开):声明类中的成员变量、属性、方法。
实现(隐藏):实现类中的方法。
接口与实现分离的好处:
想要使用这个类时只需知道接口部分即可。
同时将实现部分隐藏起来,有利于代码的封装。
[]表示调用方法
头文件中放:类的接口部分。control+command+上/下,在一个类的源文件和头文件之间快速切换。
源文件中放:类的实现部分。成员变量,实现中的成员变量对外隐藏。
接口部分:相当于说明书。
@public 之后的成员变量,是公有的,任何地方都可以访问
@protected 之后的成员变量,是受保护的,只能在类及其子类中访问,是默认值
@private 之后的成员变量,是私有的,只能在类的内部访问
@package 之后的成员变量,在当前框架中能访问
实现部分:不对外公开的成员变量,只能用在当前类的实现的方法中,权限没有意义。
-表示对象方法,通过对象调用的方法叫做对象方法。
通过类名调用的方法叫做类方法。
+表示这是一个类方法。
注意:
先声明接口在编写实现。
成员变量(属性)的声明不能带有初值。
不能为成员变量(属性)随意添加限定(static const)。
OC的对象,必须使用指针存储地址,不能创建局部对象和全局对象。[]运算符的含义是向对象发送一个消息,对象受到消息后会调用对应的方法。new这个消息的含义是创建一个对象(分配空间、成员变量初值为0)。
OC中类其实也是一种对象,所以new这个消息发送给类这种对象的,对应方法我们称之为类方法,和对象方法是不同的。
对象方法:要和函数区分:函数可直接调用,方法需要通过[]发送消息,方法内可以直接使用类(对象)中的成员变量。
一个参数的方法:
接口声明:- (void) makeCallToTel:(NSString*) tel;
方法调用:[p makeCallToTel:@"10086"];
带多个参数的方法:
接口声明:- (void) messageToTel:(NSString*) tel withMessage:(NSString*) message;
//messageToTel: withMessage:
方法调用: [p messageToTel:@"10086" withMessage:@"10000是什么?"];
16.方法中的self关键字:
方法中可以使用self关键字,是一个指针,指向当前对象。self指针的指向不能变化。
用途:
a.成员变量(属性)与参数名的区分。
b.获得当前对象的地址(如作为返回值)。
c.OC中在定义类的成员变量时习惯以_开头。
17.description方法(重写方法):
用于返回该类型的对象的描述。
a.任何一个类都可以实现这个方法(无需声明)。
b.该方法返回一个OC对象类型NSString。
c.在NSLOG()函数打印时,指定%@,将自动调用该方法并打印返回的字符串。
12.15
1.返回一个按照指定格式拼接的字符串:
NSString *p = [NSString stringWithFormat:@"%@ %@ %f %f %@", orderId,goodsName,closingPrice,freight,consigneeAddress];
2.以下划线开头定义成员变量。
3.NSInteger 是 long
CGFloat 是 double
4.复合关系:类的复合关系是类的关系中最常见的一种。复合是has a 的关系。OC中要求,所有的对象都以指针的形式引用。可能存在的问题:头文件包含的循环引用。解决问题:在头文件(接口部分)中,声明成员变量的复合关系时,尽量使用@class声明复合的类型,在源文件(实现部分)中,应当包含复合关系的头文件。
5.[new] == [[alloc] + init]
12.16
1.OC中枚举类型的命名规范:前缀是关联的类名,后面是具体的枚举类型名。枚举值的命名规范:前缀是所属的枚举类型名,后面是值的含义。
2.在类的外部想要访问成员变量,则提供方法接口。
getter方法:
作用:返回对象内部的成员变量。
命名规范:方法名与去掉下划线的成员变量名一致。
有返回值,类型与去掉下划线的成员变量名一致,不需要接收任何参数。
setter方法:
作用:提供一个方法给外界设置成员变量值,可以在方法里面对参数进行过滤。
命名规范:以set开头,后面跟成员变量名(首字母大写)。返回值一定是void,接收一个参数且与成员变量类型相同。形参名不要和成员变量一样,如果一个成员变量时只读的挥着不提供直接修改的方法,则不提供setter方法即可。
3.@property声明的被称为“属性”, 属性管理着一个成员变量以及它的getter/setter方法@property关键字声明成员变量,声明与实现getter方法和setter方法。重写setter实现方法,代替默认生成的setter方法。
4.只有成员变量和getter方法和setter方法叫做模型类。
5.@property的点语法:点语法不是取成员,是调用getter或setter方法。点语法的目的:降低对程序员的要求。
6.@property参数有四类,每一类有一个默认值,且只能有一个值,
a.是否提供setter方法
readonly:只生成get方法。
readwrite:生成set方法及get方法(默认)。
b.多线程原子操作
nonatomic:setter方法时不要加上多线程锁的代码(一般都加上)。
atomic: setter方法时加上多线程锁的代码(默认)。
c.指定setter/getter方法(更改setter/getter名字)
getter = 。。。/setter = 。。。
d.内存管理相关的
A、retain、strong(引用计数+1)
ARC使用strong,非ARC使用retain
B、assign、weak:直接赋值(引用计数不变)
非ARC使用assign
ARC中OC对象使用weak,非OC对象使用assgin
C、copy:用于深拷贝(新的),引用计数为1
一般用于NSString,NSArray等。
12.17
1.继承关系:is a
继承:A继承自B,A包含B的所有属性和方法,且A还可以包含一些额外的属性和方法。A被称作子类或派生类,B被称作父类或基类。有利于代码复用。只能继承一个(单继承),也不支持多种继承方式,可以用“协议”来代替实现。
其他说明:
a.OC中不准许子类与父类拥有相同名字的属性。
b.子类调用方法时,是先在当前类中找,找不到再到父类中找。
c.子类可以重写父类的方法,即覆盖了父类的方法,不用声明,只写实现即可。重写要求:方法名、参数、返回值与父类方法一致。有时需先执行父类方法,在执行新的方法,使用supper关键字,super关键字指向该对象的父类部分。
d.父类中的@protected属性在子类方法中可以访问,但private的不能。
NSObject是OC中所有类的父类。
2.多态:使用父类指针存储子类对象的地址。OC中自动实现多态。
3. 多态指针:
a.id是一个任意对象指针类型,NSObject*
注意与void*区分
b.instancetype与id差不多,只用于方法返回值,比id多一个类型检测的功能。
多态指针
4.对象初始化
1.类对象的new方法可以创建一个属性均为0的对象,new方法实际是两个方法的连续调用:
a.分配存储空间alloc类对象方法。
b.初始化init对象方法。
alloc和init方法都是NSObject的方法,故所有的OC类型都可以使用这种方式创建对象。
可以重写init方法,使对象初始化。
重写init方法过程:
a.调用super的init。
b.判断self,然后父类部分初始化。
自定义init方法:规范:返回值类型为instancetype 方法名以initWith开头。需要在接口中声明。
12.18
1.类在源文件中进行接口扩展,与头文件中一样,只是接口扩展不对外公开。
2.定义的类也是一种对象,是Class类的对象。
方法的调用过程:通过基类部分的isa属性,找到成员函数列表,再将消息传递的参数拿过来和函数列表中的参数对比。在自己的函数列表中找不到就到父类的函数列表中去找,直到找到为止。
3.对象方法:通过对象调用的方法,以 - 开头。
类对象方法:通过类对象调用的方法,以 + 开头。
对象方法和类对象方法可以重名。
在类方法中,self表示类对象,,指向对象本身,sper表示父类对象,self指向类对象本身super指向父类对象,类对象的self用来调用其他类方法,类方法在子类中也可以被重写。
4.class方法:变量 == 类
类对象和对象都可以用class返回class类型的类对象。
5.类对象加载:load方法,在程序运行开始时,回家再一次项目中的所有类,类加载完毕就会调用类的load方法,在程序第一次使用这个类时,会调用类的initialize方法,这两个方法是可以重写的,都是类对象方法。
注意:第一次使用该类时,会先调用该类的父类的inintialize方法,当有子类是,类和子类的load方法都会被调用;第一次使用该类时,只会调用子类的initialize方法。
6.实例化一个对象时,类方法也可以封装进行简化。
7.Foundation框架(类库、函数库等等)
a.数值类型,封装成对象。用NSNumber进行封装。就可以在很多只准许使用对象的借口中进行使用。
b.字符串对象NSString
1创建:init方法和类方法
//空串
NSString * s1 = [[NSString alloc] init];
NSString * s11= [NSString string];
//由C语言字符串创建OC字符串
NSString * s2 = [[NSString alloc] initWithUTF8String:"hello world"];
NSString * s22 = [NSString stringWithUTF8String:"hello world"];
NSString * s4 = [NSString stringWithCString:"hello" encoding:NSUTF8StringEncoding];
const char *p = s2.UTF8String;//OC字符串转C字符串
//按照指定的格式生成字符串
NSString * s3 = [[NSString alloc] initWithFormat:@"h"];
NSString * s33= [NSString stringWithFormat:@"h"];
2基本操作:
NSLog(@"%ld", s5.length);//返回字符串的长度(按照unicode编码)
unichar c = [s5 characterAtIndex:1];//返回指定下标的字符
s1 isEqualToString:s2//判断字符串是否相等
3表示数值:
NSString * s3 = @"10";
NSLog(@"%ld", s3.integerValue); //字符串转数值
double a = 123.4;
NSString * s4 = [NSString stringWithFormat:@"%lf", a];//数值转整型数
4文件操作相关:
NSString * path = @"/Users/teacher/Desktop/课堂资源/作业.txt";
NSError * err;
NSString * s5 = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err];
if ( err != nil )
{
NSLog(@"%@", err.localizedDescription);
}
[s5 writeToFile:@"/Users/teacher/Desktop/a.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL]; //文件拷贝
5URL操作相关:
NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
NSError * err;
NSString * s6 = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&err];
if ( err != nil ) {
NSLog(@"%@", err.localizedDescription);
}
NSLog(@"%@", s6);
6获得子串:
// NSString * s2 = [s1 substringToIndex:2];
// NSString * s2 = [s1 substringFromIndex:2];
NSString * s2 = [s1 substringWithRange:NSMakeRange(2, 2)];
NSLog(@"s2:%@", s2);
NSRange r = [s1 rangeOfString:@"ef"];
NSLog(@"loc:%ld len:%ld", r.location, r.length);
NSString * s3 = @"a.txt";
if ( [s3 hasSuffix:@"txt"] )
{
NSLog(@"YES");
}
判断后缀。
NSString * s3 = @"a.txt";
if ( [s3 hasPrefix:@"a.t"] )
{
NSLog(@"YES");
}
判断前缀。
NSString * s4 = @"ABCDEFH";
NSString * s5 = s4.lowercaseString;
大写转小写。
NSString * s4 = @"A123BCDEFHijklmn";
NSString * s5 = s4.uppercaseString;
小写转大写。
7构造新串:
NSString * s6 = @"hello";
NSString * s7 = [s6 stringByAppendingString:@" world"];
追加后新建一个字符串。
8字符串用于表示路径:
9可变字符串对象:NSMutableString用于描述可变字符串,是NSString的子类:
int a =123;
NSMutableString * s1 = [NSMutableString stringWithFormat:@"a=%d", a];创建可变字符串。
a追加:
[s1 appendString:@"hello"];改变了原字符串。
b删除:
[s1 deleteCharactersInRange:NSMakeRange(2, 2)];
c插入/替换:
插入:[s1 insertString:@" " atIndex:5];//a=123 a=123
替换:[s1 setString:@"hello"];
12.22
1. Foundation框架:
c. NSArray:只能存储OC对象,其他基本数据类型以及结构体都存储不了。
1创建:
NSArray* arr1 = [NSArray array];//空数组
NSArray * arr2 = [NSArray arrayWithObject:@(a)]; //单个对象元素数组
NSArray*arr3=[NSArrayarrayWithObjects:@"hello", @YES, @(a), p1, @"world", [NSNull null], nil];
//多个对象元素数组
NSArray*arr=@[@"hello",@10,@YES]; //常量方式
2数组写入文件中:
BOOL b=[arr4writeToFile:@"/Users/teacher/Desktop/a.txt" atomically:YES]; //将数组内容写入文件NSArray*arr4=[NSArrayarrayWithContentsOfFile:@"/Users/teacher/Desktop/a.txt"];//按照数组形式读取文件中的数据
3基本操作
NSLog(@"%ld", arr4.count); //数组长度
if ( [arr4 containsObject:@YES] ) //数组中是否包含
NSLog(@"firstObject:%@", arr4.firstObject); //数组中第一个元素
NSLog(@"lastObject:%@",arr4.lastObject); //数组中最后一个元素
NSLog(@"%@",arr4 [2]); //数组中第n个元素
NSLog(@"%ld", [arr4 indexOfObject:@YES]); //数组中元素的下标
NSLog(@"%ld", [arr4 indexOfObject:@NO]);//数组中元素的下标,若没有会出现一个随机数
4数组遍历
a//for循环的in语法不适合在循环过程中,改变了数组的情况
for ( id obj in arr4 )
{
NSLog(@"%@", obj);
}
b//for循环的c语言用法
for ( int i=0; i<arr.count; i++)
{
NSLog(@"%@", arr[i]);
}
5可变数组对象NSMutableArrayNSArray用于描述可变数组对象,是NSArray的子类
a添加:
NSMutableArray * arr1 = [NSMutableArray array];
[arr1 addObject:@"neusoft"];
[arr1 addObject:@YES];
[arr1 insertObject:@10 atIndex:1];
b删除:
[arr1 removeAllObjects];//删除所有
[arr1 removeLastObject];//删除最后一个
[arr1 removeObjectAtIndex:1];//删除指定下标的
12.24
1. Foundation框架:
d.字典对象NSDictionary
NSDictionary用于描述字典对象,数组的下标是整型数,字典的下标是字符串类型的key。字典是一种 key-value 形式的数组。大部分情况下,都使用字符串的对象作为key。字典中的key唯一。 NSDictionary 不可变字典对象
NSMutableDictionary 可变字典对象
和数组对象一样,字典中只能存储OC对象
1创建:
NSDictionary * dict1 = [NSDictionary dictionary];//空字典
NSDictionary * dict2 = [NSDictionary dictionaryWithObject:@30 forKey:@"age"];
//传递一个value的字典
NSDictionary * dict3 = [NSDictionary dictionaryWithObjectsAndKeys:@30, @"age", @"neusoft", @"name", nil]; // 传递多个value的字典,以key-value形式传递
NSDictionary * dict4 = [NSDictionary dictionaryWithObjects:@[@30, @"neusoft"] forKeys:@[@"age", @"name"]];// 传递多个value的字典,以key和value分别的形式传递
NSDictionary * dict5 = @{@"age":@30, @"name":@"neusoft"};// 传递多个value的字典,以基本形式传递
2文件操作:
[dict5 writeToFile:@"/Users/teacher/Desktop/a.txt" atomically:YES]; //将字典中的内容写入文件中。
NSDictionary * dict1 = [NSDictionary dictionaryWithContentsOfFile:@"/Users/teacher/Desktop/a.txt"];//将文件中的字典内容读出。
3长度与遍历:
NSLog(@"%ld", dict1.count); //字典的长度
for ( NSString * key in dict1 )
{
NSLog(@"%@ = %@", key, dict1[key]);
}//字典的遍历
4字典与数组结合:
NSArray * a1 = @[
@{@"name":@"aaa", @"age":@10},
@{@"name":@"bbb", @"age":@20},
@{@"name":@"ccc", @"age":@30}
]; //数组嵌套字典
NSDictionary * d1 = @{
@"name":@"aaa",
@"age":@10,
@"score":@[@80, @78, @12, @67]
};//字典嵌套数组
网络中JSON->OC对象 数组与字典嵌套的格式
e.字典对象与模型对象的转换
从本地或者网络获取的数据,往往都是字典类型的。
这些字典数据往往字段固定,且以数组的形式存储。
使用字典可能带来的不利:
1key不方便记忆且编译器不进行正确性的验证。
2不能进行一些对象操作。
解决方法是:将固定字段的字典,封装成自定义对象,这种对象我们称之为“模型”。
字典转模型的步骤
1创建模型类,属性名对应字典中的key字符串中的名字,而属性类型应为字典中对应value的类型。
2添加init及类方法创建模型类。
3在持有模型对象的类中添加属性,往往是数组。
4将字典转成模型对象,属性存储。
f.关于懒加载:
是一种延迟加载方式,在需要的时候才加载。
具体做法:重写属性的getter方法。
模型转字典:使用模型对象的方法。
12.25
1. Foundation框架:
g.KVC Key Value Coding 键值编码,提供一种机制来间接访问属性。要注意与字典的操作区分 key-Object。
意义:
1提供了对象属性的另一种有效的访问方式。
2为KVO(观察者设计模式)提供了实现基础。
1//setter方法与KVC
p.name = @"neusoft";
[p setValue:@"neusoft" forKey:@"name"];
2//getter方法与KVC
NSLog(@"%@", p.name);
NSLog(@"%@", [p valueForKey:@"name"]);
3//KVC中使用setValuesForKeysWithDictionary:方法对对象的所有属性赋值
[p setValuesForKeysWithDictionary:@{@"name":@"neusoft", @"age":@10}];//字典转模型
NSDictionary * d1 = [p dictionaryWithValuesForKeys:@[@"name", @"age"]];//模型转字典
h.集合类NSSet NSMutableSet
与NSArray的区别:
1集合不能通过下标取成员
2集合类中的对象不能重复
i.NSData 数据对象(缓冲区对象) NSMutableData
NSData一般而言,从网络上获取的数据都是NSData对象。
j.NSFileManager
用于管理文件系统
常见操作:常见、删除一个文件或目录 获得目录中的文件。
类方法defaultManager用于获取对象,不要使用alloc+init new。----单例设计模式。
k. 时间操作对象
1 NSData 用于描述日期时间,提供一些基本的时间获取和比较。
2 NSDateFormatter 用于字符串格式化NSDate。
3 NSCalendar对象 用于将NSData 对象应用在日历上。
4 NSDateComponents对象 用于对象的方式描述NSData对象。
l. 几何数据结构体 可以封装到NSValue对象中,NSValue是NSNumber的父类
1 NSRange 表示范围
2 CGSize 表示二维尺寸
3 CGPoint 表示位置
4 CGRect 表示矩形的尺寸以及位置
12.28
1.不可变数组实现(单向链表)
#import "AMArray.h"
#include <stdlib.h>
@implementation AMArray
{
void ** _objArray;
}
+ (instancetype) arrayWithObject:(id) obj
{
return [[self alloc] initWithObject:obj];
}
- (instancetype) initWithObject:(id) obj
{
if ( self = [super init] ) {
_objArray = (void**)malloc(sizeof(void*)*1);
*_objArray = (__bridge void *)(obj);
_count = 1;
}
return self;
}
- (instancetype) initWithObjects:(id) firstObj, ...
{
if ( self = [super init] )
{
if ( firstObj )
{
NSMutableArray * arr = [NSMutableArray arrayWithObject:firstObj];
va_list params; //变参列表指针
va_start(params, firstObj);//开始解析变参
while (1)
{
id a = va_arg(params, id);
if ( a == nil )
{
break;
}
[arr addObject:a];
}
va_end(params);//解析结束
//arr中存储所有的变参
_count = arr.count;
_objArray = (void**)malloc(sizeof(void*)*_count);
for ( int i=0; i<_count; i++)
{
_objArray[i] = (__bridge void *)(arr[i]);
}
}
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
if ( index >= _count) {
return nil;
}
return (__bridge id)(_objArray[index]);
}
- (BOOL)containsObject:(id)anObject
{
for ( int i=0 ;i<_count; i++) {
if ( anObject == _objArray[i] ) {
return YES;
}
}
return NO;
}
2.可变数组实现(单向链表)
#import "AMMutableArray.h"
typedef struct arr_node {
void* data;
struct arr_node * next;
}sArrNode, *pArrNode;
//sArrNode == struct arr_node
//pArrNode == struct arr_node * == sArrNode *
@implementation AMMutableArray
{
pArrNode _phead;
pArrNode _ptail; //指向最后一个结点
}
+ (instancetype)array
{
return [[self alloc] init];
}
- (instancetype)init
{
if ( self = [super init] )
{
_count = 0;
//头结点:不存储数据的结点
_phead = (pArrNode)malloc(sizeof(sArrNode));
_phead->next = NULL;
_phead->data = NULL;
//保证_ptail存储最后一个结点的地址
_ptail = _phead;
}
return self;
}
- (void)addObject:(id)obj
{
pArrNode pnew = (pArrNode)malloc(sizeof(sArrNode));
pnew->data = (__bridge void *)(obj);
pnew->next = NULL;
_ptail->next = pnew;
_ptail = pnew;
_count++;
}
- (void)insertObject:(id)obj atIndex:(NSUInteger)index
{
if ( index > _count )
{
return ;
}
pArrNode pnew = (pArrNode)malloc(sizeof(sArrNode));
pnew->data = (__bridge void *)(obj);
pnew->next = NULL;
pArrNode ptrav = _phead;
for ( int i=0; i<index; i++)
{
ptrav = ptrav->next;
}
pnew->next = ptrav->next;
ptrav->next = pnew;
if ( ptrav == _ptail )
{
_ptail = pnew;
}
_count++;
}
- (NSString *)description
{
NSMutableString *s = [NSMutableString stringWithFormat:@"(\n"];
for (pArrNode ptrav = _phead->next;
ptrav != NULL;
ptrav = ptrav->next)
{
[s appendFormat:@" %@,\n", (__bridge id)(ptrav->data)];
}
[s appendString:@")"];
return s;
}
- (id) objectAtIndex:(NSUInteger) index
{
pArrNode ptrav = _phead->next;
for ( int i=0; i<index; i++)
{
ptrav = ptrav->next;
}
return (__bridge id)(ptrav->data);
}
- (void)removeLastObject
{
pArrNode ptrav;
for (ptrav = _phead; ; ptrav=ptrav->next)
{
if ( ptrav->next == _ptail )
{
break;
}
}
ptrav->next = NULL;
free(_ptail);
_ptail = ptrav;
_count--;
}
- (void)removeObjectAtIndex:(NSUInteger)index
{
if ( index >= _count )
{
return;
}
pArrNode ptrav = _phead;
for (int i=0; i<index; i++)
{
ptrav= ptrav->next;
}
pArrNode pdel = ptrav->next;
ptrav->next = pdel->next;
free(pdel);
if ( index == _count-1)
{
_ptail = ptrav;
}
_count--;
}
- (void)removeAllObjects
{
while ( _count != 0 )
{
[self removeLastObject];
}
}
12.29
1.单链表:数据域、指针域(下一个结点的地址)。
头结点:是一个不存储数据的结点,起作用是方便管理链表,当头结点的指针域为NULL,意味着链表为NULL。
双链表:两个指针域(下一个结点的地址、上一个结点的地址)。
双循环:两个指针域、首尾相连(头结点的上一个结点是尾结点,尾结点的下一个结点是头结点)。
链表优点:长度不固定,且增加、删除数据效率高。
数组优点:长度固定,访问数据效率高。
插入时:先连接被插入结点中的指针,先连接后继指针再连接前驱指针。
将pinsert插入到pback的后面
pinsert->prev = pback;
pinsert->next = pback->next;
pback->next->prev = pinsert;
pback->next = pinsert;
将pdel从链表中拿掉
pdel->prev->next = pdel->next;
pdel->next->prev = pdel->prev;
pdel->prev = pdel;
pdel->next = pdel;
链表的遍历
for(pArrNode ptrav = phead->next;ptrav != phead;ptrav = ptrav->next); //查找
for(pArrNode ptrav = phead;ptrav->next != phead;ptrav = ptrav->next); //插入删除
for(pArrNode ptrav = phead->ptrav;ptrav != phead;ptrav = ptrav->ptrav); //倒叙遍历
12.30
1.存储方式:
连续存储(数组)
链式存储(链表)
离散存储(哈希)
2.hash:
哈希函数:所得结果的个数,一定是有穷的。
哈希表(数组):长度 == 哈希函数所得到结果的个数。
哈希冲突:
3.判断单向链表是否环。
int HASH_FUNC()
{
p1 = phead;
p2 = phead;
while(1)
{
p1 = p1->next;
p2 = p2->next;
if(p2 == NULL)
{
return 0;
}
p2 = p2->next;
if(p2 == NULL)
{
return 0;
}
if(p1 == p2)
{
return 1;
}
}
}
4.有一个单向不循环链表,有一个指针指向一个结点,删除这个结点。
将后面的数据域整体向前移,删除最后一个结点。
5.删除非循环单向链表中倒数第K个结点,要求时间复杂度为0(n)
pArrNode p1 = phead,p2 p1 = phead;
int i = 0;
while(1)
{
i++;
p1 = p1->next;
if(i > k)
{
p2 = p2->next
}
if( p1 == NULL)
{
break;
}
}
5.请用最快的速度找到单向非循环链表中的中间结点。
两个指针一个走两步一个走一步,走两步到尾结点,走一步到中间结点。
6.海量数据问题
1Top K问题:
从海量数据中找出频率最高的K个元素(搜索引擎的统计、微博热点话题、歌曲的排行榜等等)
解决思路:hash 1000份 1万条
求出每一份中频率最高的K个,1000*K个
2找出或删除重复元素
解决思路:位图法
100亿个整型数
10G 1bit
0~4G-1
char arr[512];
3排序
解决思路:位图法
12.31
1.分类:对现有类的扩展,分类只扩展方法,不扩展属性(成员变量),不能改变类的代码。
要使用分类的方法,必须包含分类的头文件。
用途:
1将不同作用的代码分散到不同的文件中,是代码结构更清晰。且多人开发一个类代码时,方便共同开发。
2对现有的类进行扩充。
3也可用于覆盖类中原有的方法,和子类的重写功能类似。
使用@property,仅仅是声明getter方法,没写实现,可以使用点语法使用。
2.SEL:方法的调用实际是发送SEL消息。
SEL是对方法的一种包装,所以说发消息,就是发送SEL数据。
将方法包装成一个SEL类型的数据,去找对应的方法地址,找到方法地址就可以调用方法。
调用方法的过程:
1将test包装成SEL。
2根据SEL数据找到对应方法的地址。
3根据方法地址调用对应的方法。
这个操作是由缓存的,能提高查找效率。实际上SEL是一个结构体指针。
每个方法中都有一个_cmd代表当前方法。
3.block:是一种数据类型,在iOS开发中广泛使用且苹果公司极力推荐使用,其功能很像C语言中的函数指针。^是block得特有标志。block的实现代码包含在{}中。大多数情况下,以内联的形式被定义及使用。block的使用和函数指针的使用类似。block内部可以访问外部的变量。外部的局部变量,block内部不能修改,全局变量可以。如果局部变量使用_block声明,则可以在内部修改。实际上,定义block时会对局部变量进行拷贝(副本)block的重要意义:在处理异步任务时非常方便。
4.新语法:
1nullability特性:nullable和nonnull
2轻量级泛型
3kindof
1.04
1.协议:可以用来声明一大堆方法(不能声明成员变量),只要某个类遵循了这个协议,就相当于拥有了这个协议中的所有方法声明,只要父类遵循了某个协议,就相当于子类也遵循。NSObject协议是基协议,Objective-C要求所有的协议1必须遵循基协议,一个类可以遵循多个协议。协议中的两个关键字:@required其后的方法必须实现,@optional其后的方法可选是否实现。协议本身似于C++中的抽象类,遵循协议,实现了部分多继承的功能,为代理设计模式,提供了基础。
2.NSCopying协议:一个对象调用copy,实际上是调用了copyWithZone:,该类型应当遵循<NSCopying>并实现copyWithZone:方法,一个对象调用mutablecopy,实际上是调用了mutablecopyWithZone:,该类型应当遵循<NSMutableCopying>并实现mutablecopyWithZone:方法。
3.深复制和浅复制
a.深复制:内容的拷贝,源对象和副本对象指向不同的对象,源对象的引用计数不变,副本的引用计数为1。
b.浅复制:指针的拷贝,源对象和副本对象指向同一个对象,对象的引用计数+1,相当于做了一次retain。(内存管理)
4. NSCoding协议(归档存储):Foundation框架中的基本数据类型,如:NSString NSNumber NSArray NSDictionary ...,可以进行归档存储,如果是一个自定义类型的对象,该类型需要遵循NSCoding协议。在归档时,会调用encodeWithCoder:方法,在解档时,会调用initWithCoder:方法。如果一个类型的对象,需要进行归档存储,该类型遵循协议<NSCoding> 并实现两个方法。NSCoder是编码器对象。
1.5
1.内存管理:
a.管理范畴:OC对象。
b.管理方式:手动、半自动、自动
在需要时分配,用完之后释放,不要使用任何已释放的内存资源,否则会发生错误。
c.引用计数与内存管理准则:
1retain方法 retainCount+1
2release方法 retainCount-1
3retainCount为引用计数,当值为0时,自动释放
4准则:有始有终,有加有减
a 只要还有人在使用某个对象,那么这个对象就不会被回收,只要你想使用这个对象,那么就应该让这个对象的引用计数器+1,当你不想使用这个对象时,应该让对象的引用计数器-1.
b谁创建,谁release:
如果你通过alloc,new,copy来创建一个对象,那么你就必须调用release或autorelease方法,不是你创建的,就不用你去负责。
c谁retain,谁release。
d.使用引用计数:
1当创建一个对象时,该创建对象的引用计数为1。
2当某段代码要访问这个对象时就对该引用计数+1。(retain)
3当这段代码完成后要对这个引用计数-1。(release)
4当引用计数为0时,表示没有代码访问这个对象了,该对象就会被销毁。(销毁前自动向其发送一个dealloc消息)
e.MRC中的代码规范:
1只要调用alloc,就必须有release。
2属性的setter方法:
a基本数据类型直接赋值
bOC对象,先判断和属性旧值的是不是同一对象,如果是,则什么也不做;如果不是,将旧值release,并对新值retain。
属性如果是字符串对象(copy)---属性的深拷贝
3dealloc方法:
a对self所持有的属性release一次
b[super dealloc]放在最后
- (void)dealloc
{
[_car release];
[super dealloc];
}
4不要出现垃圾代码。(无法释放的代码)
f.三个参数:
assgin关键字 生成的setter方法,直接赋值。
- (void)setAge:(NSInteger)age
{
_age = age;
}
retain关键字 生成的setter方法,进行引用计数操作。
- (void)setCar:(AMCar *)car
{
if ( _car != car ) {
[_car release];
_car = car;
[_car retain];
}
}
copy关键字 一般用于字符串,生成setter方法,新建一个对象(深复制)。
- (void)setName:(NSString *)name
{
if ( _name != name ) {
[_name release];
_name = [name copy];
}
}
1.6
1.半自动释放池:
autorelease方法:此方法将对象放到自动释放池中,自动释放池被销毁,同时自动释放池中的所有对象release一次,该方法不改变引用计数。
@autoreleasepool{}声明了一个自动释池的管理范围,其后de{…}相当于自动释放池的生存期。
将autorelease方法封装到类方法中是一个比较好的做法。
2.ARC机制和判断准则:
a.ARC禁用的方法:retainCount retain release autorelease
b.在重写dealloc时,不必添加[super dealloc]
c.ARC下对象销毁的判断准则:
1当没有强指针指向这个对象时,对象销毁
2OC中,指针默认就是强指针
3涉及的关键字: __strong __weak
3.ARC下的内存管理参数:
assign --> 非OC对象。
strong --> OC对象,管理的成员变量是强指针,相当于retain。
weak --> OC对象,管理的成员变量是弱指针。
copy --> NSString对象。