我的盲区不一定是你的盲区,欢迎copy。
但是闲下来不要忘记还有很多你了解不够透彻的。
点语法的死循环注意
在set方法和get方法别用点语法,否则会产生死循环。以下两个方法会产生死循环
- (void)setAge:(int)age{
self.age = age;
//展开成:[self setAge:age];
}
- (int)age{
return self.age;
//展开成:return [self age];
}
SEL的概念
在Objective-C中,SEL是选择器(selector)的一个类型。选择器就是指向方法的一个指针。
可以简单理解成:SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据。去找对应的方法地址。找到方法地址就可以调用方法。
调用
//Person类中有 +test1方法和 -test2方法
Person *p = [Person new];
//调用对象方法 -test2 方法1
[p test2]
//方法2:利用sel间接调用
[p performSelector:@selector(test2)];
若调用的方法需要传参数
[p test3:@"chenfanfang"];
//或者用sel方法
[p performSelector:@selector(test3:)withObject:@"chenfanfang"];
//注意:方法若有参数,则写方法名不能漏了冒号:
创建SEL类型的数据
SEL s = @selector(test3);
copy,retain,assign的区别
retain :release旧值,retain新值(适用oc对象类型)。retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的引用计数+1(引用计数变为2)。
assign :默认属性 直接赋值,不改变引用计数(retainCount),适用于非oc对象类型,用于基础数据类型 (NSInteger)和C数据类型(int,float, double, char,等),其中int这样的基本类型并不是对象,如果使用retain与copy会报错。
copy :release旧值,copy新值。copy是创建一个新对象,而retain是创建一个指针使对象引用计数加1。Copy属性表示两个对象内容相同,新的对象引用计数为1 ,与旧有对象的引用计数无关,旧有对象没有变化。copy减少对象对上下文的依赖。(如果牵扯到上面程序中两个值互相影响的情况,应该用copy)
总结一下,retain 是指针拷贝,copy 是内容拷贝。
基本的数据类型
id类型:类似Java 的Object 类,可以转换为任何数据类型,id类型已经是指针了,所以变量不需要加*
id foo=nil;
atomic,nonatomic readonly,readwrite
atomic是默认属性,保证setter/getter的原子性,即线程同步。nonatomic不保证原子性
readwrite为默认属性,生成默认的setter/getter方法。readonly只生成getter方法。
权限问题
private(自己):该类中的方法可以访问这样的变量,但不能被子类定义的方法直接访问。
protected(自己与子类):该类和任何的子类中的方法可以直接访问这样的变量,这是默认的。
public(自己,子类,其他类):除了自己和子类中的方法外,也可以被其他类或者其他模块中的方法所访问。开放性最大,但最好避免使用这个作用域。其他类应该使用getter/setter方法来访问或设置其他类上的实例变量,确保封装性。
package:
注:默认的变量权限为protected,方法只能为public
内存管理
概念
1.oc使用“引用计数(retainCount)”方式来管理内存。当该对象创建时,该计数为1,说明有一个引用,如果不用时,就会减为0,这就表示不再使用了,那么就会销毁该空间。
方法:
1.alloc, allocWithZone,new(带初始化)
为对象分配内存,retainCount为“1”,并返回此实例。
2.retain
retainCount 加“1”
3.copy,mutableCopy
复制一个实例,retainCount数为“1”,返回此实例。所得到的对象是与其它上下文无关的,独立的对象(干净对象)。
4.release
retainCount 减“1”,减到“0”时调用此对象的dealloc方法
5.autorelease
把当前对象放入了当前的Autoreleasepool中,当该pool被释放时,该pool中的所有Object会被调用Release
我们可以把上面的接口按对retainCount的操作性质归为两类,
A类是加一操作:1,2,3
B类是减一操作:4,5(延时释放)
内存管理准则如下:
1,A与B类的调用次数保持一制
2,为了很好的保障准则一,以实例对象为单位,谁A了就谁B,没有第二者参与
内存管理ARC
概念:
- 从XCode4.2开始就引入Automatic Reference Counting机制。我们创建的模板能看到@autoreleasepool{};
- ARC使得你不需要再思考何时使用retain,release,autorelease这样的函数来管理内存,它提供了自动评估内存生存期的功能,并且在编译期间自动加入合适的管理内存的方法。编译器也会自动生成dealloc函数。
基本的ARC使用规则:
1.代码中不能使用 release, retain, autorelease,dealloc, retainCount
2.不重载dealloc(如果是释放对象内存以外的处理,是可以重载该函数的,但是不能调用[super dealloc])
3.不能使用NSAllocateObject, NSDeallocateObject
4.不能在C结构体中使用对象指针
5.id与void *间的如果cast时需要用特定的方法(__bridge关键字)
6.不能使用NSAutoReleasePool、而需要@autoreleasepool块
7.不能使用“new”开始的属性名称
property 内存管理策略的选择
1、非ARC下
①copy:只用于NSString、block
②retain:除NSString、block以外的OC对象
③assign:基本数据类型、枚举、结构体(非OC对象)、当两个对象相互引用时,一端用retain,一端用assign
2、ARC下
①copy:只用于NSString、block
②strong:除NSString、block以外的OC对象
③weak:当两个对象相互引用,一端用strong,一端用weak
④assign:基本数据类型、枚举、结构体(非OC对象)
字典类型 NSDictionary 和 NSMutableDictionary
概念
1.字典类型 NSDictionary 和 数组NSArray 类型相似,但存在数组里要取值每次都要遍历,这样就浪费了很长的时间,字典的便利就在于他在存取对象的时候,在后面会追加一个 键值,可以理解成一个标志,我们可以根据这个标志很快的找到这个对象,这样就相对于数组的全部遍历就要方便多。
2.字典类型与数组类型相似,都是存取的对象,不能存取基本的数据类型,如 int , double, char等,类比数组,字典自然也会有两种类型,不可变字典: NSDictionary 和 可变字典 NSMutableDictionary
3.字典中的数据是以 键值对 的形式出现,并且顺序不能乱,前边是对象,后面是键名
NSDictionary
创建:
1.NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:student1,@"小明",student2,@"小刚",student3,@"小红",nil];
2.NSDictionary * dic = @{student1:@"小明",student2:@"小刚",student3:@"小红"};
使用
NSArray *arrayA =[NSArrayarrayWithObjects:@"B",@"A",@"E",@"D",@"C", nil];
NSDictionary *dic1 = [[NSDictionary alloc] initWithObjectsAndKeys:arrayA,@"name",@"1234",@"tel",nil];
//全部的key 这个结果的顺序不是固定的,因为查找的方法为哈希查找,所以数组的顺序有可能不是你所定义的顺序
NSArray *keysAll = [dic1 allKeys];
//全部的value 这个结果的顺序也不是固定的
NSArray *valuesAll = [dic1 allValues];
//取出值为Yue的所有key值
NSArray *key = [dic1 allKeysForObject:arrayA];
//取出key值为name的对象(万物皆对象)
NSArray *value = [dic1 objectForKey:@"name"];
NSMutableDictionary
创建:
NSArray *arrayA = [NSArray arrayWithObjects:@"B",@"A",@"E",@"D",@"C", nil];
NSArray *arrayB = [NSArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
NSMutableDictionary *dic2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:arrayA,@"hong",@"stu2",@"huang",nil];
使用:
//删除key值为huang的value值 连同key一起删
[dic2 removeObjectForKey:@"huang"];
//删除全部
[dic2 removeAllObjects];
//添加key值为book的value:arrayB
[dic2 setObject:arrayB forKey:@"book"];
NSArray
创建:
NSArray *arrayA = [NSArrayarrayWithObjects:@"B",@"A",@"E",@"D",@"C", nil];
NSArray *arrayB = [NSArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
NSLog(@"arrayA:%@,arrayB%@",arrayA,arrayB);
//1、获取数组中总共有多少个对象。
NSLog(@"个数:%lu",(unsigned long)arrayA.count);
NSLog(@"个数:%lu",(unsigned long)[arrayB count]);
//2、获取数组中下标对应的元素对象.(下标是从0开始)
NSLog(@"获取下标为1的元素:%@" ,[arrayA objectAtIndex:1]);
//3、在当前数据中追加一个新的对象,并且返回一个新的数据对象(新的数组对象和被追加的对象,是两个不同的数组对象)。
NSLog(@"重组数组 组成新数组:%@" ,[arrayA arrayByAddingObject:@"F"]);
//4、在当前的数组中追加一个新的数据,并且返回一个新的数组对象。
NSLog(@"2个数组重组组成新数组:%@",[arrayA arrayByAddingObjectsFromArray:arrayB]);
//5、使用当前的数组生成一个字符串,新生成的字符串使用提供的separator字符进行分割。
NSLog(@"分割成字符串%@",[arrayA componentsJoinedByString:@","]);
//6、检测数据中是否包含指定的对象元素
NSLog(@"arrayA是否有1:%i,arrayA是否有A:%i",[arrayA containsObject:@"1"],[arrayA containsObject:@"A"]);
//7、使用当前的数组生成字符串。允许一个对象返回一个字符串来描述它的内容;这个常用于调试debugging
NSLog(@"重写改变生成字符串:%@",[arrayA description]);
// 8、根据设置的locale 进行连接数组
// NSLog(@"连接数组1:%@",[arrayA descriptionWithLocale:@"4"]);
// NSLog(@"连接数组2:%@",[arrayA descriptionWithLocale:arrayB indent:1]);
//9、两个数组的第一个元素是否相同,如果相同,则返回 数组中,第一个元素的字符串,反之,返回null 对象
NSLog(@"两个数组的第一个元素是否相同:%@",[arrayA firstObjectCommonWithArray:arrayB]);
//10、 从数组中获取NSRange对象的数据存放到objects 中,NSRange的数据标示从location,开始后面length 个数据
//-(void)getObjects:(id__unsafe_unretained [])objects range:(NSRange)range;
//11、 判断制定的anObject 对象是否存在数组中如果存在返回,对象所在的下标
NSUInteger index= [arrayA indexOfObject:@"C"];
if (index == NSNotFound) {
NSLog(@"不在");
}else{
NSLog(@"对象所在的下标:%lu",index);
}
//11-1、 判断制定的元素,是否在数组中,数组查询的位置,是从range.location 的位置开始,到range.length 的长度结束
NSUInteger index1= [arrayA indexOfObject:@"C" inRange:NSMakeRange(0, 2)];
if (index1 == NSNotFound) {
NSLog(@"不在");
}else{
NSLog(@"对象所在的下标1:%lu",index1);
}
//12、比较两个数组是否相同 ,数组长度相同,并且相同位置上的元素也相同。
NSLog(@"两个数组是否相同:%i",[arrayA isEqualToArray:arrayB]);
//13、返回最有一个元素,如果一个数组的长度为0 返回的对象为nil
NSLog(@"arrayA最后一个:%@,arrayA第一个:%@",[arrayA lastObject],[arrayA firstObject]);
// 15、使用数组返回一个 NSEnumerator 对象,这个对象类似与一个指针,可以用来遍历 整个数组 指针从前向后遍历
// NSEnumerator *enu = [arrayA objectEnumerator];
// NSLog(@"enu:%@",enu);
//这是用来排序的函数,comparator 这个参数,需要传入一个返回结果是NSComparisonResult 的函数,
NSArray *a = [arrayA sortedArrayUsingSelector:@selector(compare:)];
NSArray *b = [arrayA sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
NSArray *c = [arrayA sortedArrayUsingSelector:@selector(localizedCompare:)];
NSLog(@"aaaa:%@,bbbb:%@,cccc:%@",a,b,c);
//21、用来获取数组中range.location 开始,数据各数 为range.length 的数据,并放置到一个新的数组中
NSLog(@"arrayA第1个起后2个数%@",[arrayA subarrayWithRange:NSMakeRange(1, 2)]);
//22、写入数组中的数据,到指定path 的目录中:
BOOL q = [arrayA writeToFile:@"path" atomically:YES];
NSLog(@"%i",q);
//23、如同上面的方法一样,所不同的是写入数组中的内容到 网上指定的路径。
BOOL q1 = [arrayA writeToURL:[NSURL URLWithString:@"网上的路径"] atomically:YES];
NSLog(@"%i",q1);
//26、 用来根据indexes 获取一个数组, NSIndexSet 是一个用来管理 index 的对象。
NSLog(@"arrayA第一个:%@",[arrayA objectAtIndex:1]);
NSIndexSet *se = [NSIndexSet indexSetWithIndex:2];
NSIndexSet *se1 = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 3)];
NSArray *test = [arrayA objectsAtIndexes:se];
NSArray *test1 = [arrayA objectsAtIndexes:se1];
NSLog(@"se:%@,se1:%@,test:%@,test1:%@",se,se1,test,test1);
//27、返回指定下标的一个对象。这个方法类似 objectAtIndex:
NSLog(@"arrayA第4个:%@",[arrayA objectAtIndexedSubscript:4]);
//28、使用block 块遍历整个数组。这个block 需要三个参数,id obj 表示数组中的元素。
//NSUInteger idx 标示元素的下标,
//bool *stop 是一个bool类型的参数
[arrayA enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@,%lu",obj,(unsigned long)idx);
}];
//Options:遍历方向 反向
[arrayA enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@,%lu",obj,(unsigned long)idx);
}];
//30、 同上面的方法一项,不过NSIndexSet 参数标示,根据下标取出的数组,这里真正在block 中遍历的数组,是根据NSindexSet 取到的子数组 w
[arrayB enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 3)] options:NSEnumerationConcurrent usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@,%lu",obj,(unsigned long)idx);
}];
//31、 根据条件用来获取一个NSUIndex 对象,主要是根据条件进行数据遍历使用
NSInteger index11 = [arrayA indexOfObjectPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([@"A" isEqualToString:obj]) {
NSLog(@"YESSSSS");
return YES;
}else{
NSLog(@"NOOOOO");
return NO;
}
}];
NSLog(@"index11:%ld",(long)index11);
//33 、 这个方法添加了参数,用来表示,是从前向后,遍历还是从后向前遍历 执行了arrayA.count次
NSInteger index12 = [arrayA indexOfObjectWithOptions:NSEnumerationReverse passingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([@"1" isEqualToString:obj]) {
NSLog(@"YES");
return YES;
}else{
NSLog(@"NOO");
return NO;
}
}];
NSLog(@"index12:%ld",(long)index12);
NSIndexSet *index33 = [arrayA indexesOfObjectsWithOptions:NSEnumerationReverse passingTest: ^ BOOL (id tr, NSUInteger index,BOOL *te){
if([tr isEqualToString:@"E"]){
return YES;
}
return NO;
}];
NSLog(@"index33:%@",index33);
//35、对数组进行排序操作 参数cmptr 是一个block 函数块,返回的数据类型是一个NSComparisonResult 对象
NSArray *comparator = [arrayA sortedArrayUsingComparator:^NSComparisonResult(NSString *s1, NSString *s2) {
//characterAtIndex:获取指定位置的字符,位置从0开始递增。
if ([s1 characterAtIndex:0] < [s2 characterAtIndex:0]) {
return NSOrderedAscending;
}else if ([s1 characterAtIndex:0] > [s2 characterAtIndex:0]){
return NSOrderedDescending;
}else{
return NSOrderedSame;
}
}];
NSLog(@"comparator:%@",comparator);
//36、进行排序操作,NSSortOptions 排序的参数 用来表示是同时排序,还是稳定执行。
NSArray *test111 = [arrayA sortedArrayWithOptions:NSSortStable usingComparator:^ NSComparisonResult (NSString *s,NSString *s2){
//s.length 表示s的长度 一个中文也算1
if(s.length < s2.length){
return NSOrderedAscending;
}
if(s.length > s2.length){
return NSOrderedDescending;
}
return NSOrderedSame;
}];
NSLog(@"test111:%@",test111);
基础框架类
NSData:序列字节,不可修改。要修改的话需要用到NSData子类NSMutableData。NSData主要用于从网上下载数据(以NSData对象返回数据);把对象存储在文件中;读取文件数据(将NSData对象当做缓存区)。
NSSet:无序的不同对象的集合,不可变,可通过调用方法增加或删除一个集合来获得新的NSSet对象。它的子类是NSMutableSet。
NSDictionary:其键值对的无序集合,键通常是NSString,值可以是任何一个对象,NSDictionary是定长的,它的变长子类是NSMutableDictionary。
属性列表
NSObject:每个类都是从NSObject继承来的,它采用 NSObject协议,它实现了与 NSCopying、 NSMutableCopying和 NSCoding 协议相关的方法,
继承,重写覆盖,重载
方法重写:若子类中的方法与父类的某一方法具有相同的方法名、返回类型和参数表,则新方法覆盖原有方法。
方法重载:类中可以创建多个方法,他们具有相同的方法名,但具有不同的参数和不同的定义,调用方法时通过传递给他们不同个数和类型的参数来决定使用哪个方法。 方法名一定相同;方法的参数表必须不同,包括参数的个数和类型,以此区分不同的方法体; 方法的返回类型和修饰符可以相同也可以不同。
self:一个类中的方法调用同一个类的另一个方法是使用self,代表本身,相当于this。
super:表示父类,可以使用super访问父类中被子类隐藏或重写的方法。
1.oc中只能单继承,即一个类只有一个父类
2.子类可以重写父类的方法,对其进行覆盖。
子类内部还能调用父类的该方法,使用super
[super method];
3.oc不支持重载,即使参数不一样也不能使用同名方法
异常处理语法:
@try {
//可能发生异常的代码
}
@catch (NSException *指针名) {
//发生异常后执行的代码
}
@finally {
//无论有没有发生异常,都会执行的代码
}
对象与类
1.对象方法和类方法的方法名可以同名,两个方法互不影响。
2.类方法调用不需要创建对象,从效率上而言会更快一些。
3.对象方法只能通过对象名来调用,类方法只能通过类名来调用。
面向对象设计原则
1.耦合度 当修改一个对象的时候,对另外一个对象的影响程度
2.高内聚 一个对象仅仅做自己相关的事情
注:面向对象设计原则是高内聚、低耦合
NSString
拼接,合并,转换
//经典的字符串赋值
NSString *str0 = @"my name is justcoding !";
//字符串格式化合并分别包括
//NSString*类型 int类型 char*类型
NSString *str1 = [NSString stringWithFormat:@"我的名字:%@ 我的年龄:%d 我的邮箱:%s",@"justcoding", 25,"justcoding@gmail.com"];
//字符串赋值 参数中只可以写一个字符串 和第一种很像
NSString *str2 = [NSString stringWithString:@"我是字符串"];
//字符串转换为utf-8格式 参数为char*类型
NSString *str3 = [NSString stringWithUTF8String:"字符串转换utf-8格式"];
//字符串合并
int i = 100;
char*c = "xuanyusong";
NSString *temp = @"我是临时字符串";
//在字符串temp的基础继续添加 int i 与 char* c 组成一个新的字符串
NSString *str4 = [temp stringByAppendingFormat:@"整型: %d 字符型 :%s",i,c];
//在字符串temp的基础继续添加temp 并组成一个新的字符串
NSString *str5 = [temp stringByAppendingString:temp];
遍历
//经典的字符串赋值
NSString *str = @"YUSONGMOMO";
//字符串的长度
int count = [str length];
NSLog(@"字符串的长度是%d",count);
//遍历字符串中的每一个字符
for(int i =0; i < count; i++)
{
char c = [str characterAtIndex:i];
NSLog(@"字符串第 %d 位为 %c",i,c);
}
比较
NSString *str0 = @"justcoding";
NSString *str1 = @"justcoding";
//字符串完全相等比较
if([str0 isEqualToString:str1])
{
NSLog(@"字符串完全相等");
}
//字符串以开头比较
if([str0 hasPrefix:@"just"])
{
NSLog(@"字符串str0以just开头");
}
//字符串以结尾比较
if([str1 hasSuffix:@"coding"])
{
NSLog(@"str1字符串以coding结尾");
}
//考虑大小写的比较
NSString *astring01 = @"This is a String!";
NSString *astring02 = @"this is a String!";
BOOL result = [astring01 compare:astring02] == NSOrderedSame/NSOrderedDescending(降)/NSOrderedAscending(升);
NSLog(@"result:%d",result);
//NSCaseInsensitiveSearch:不区分大小写比较 NSLiteralSearch:进行完全比较,区分大小写 NSNumericSearch:比较字符串的字符个数,而不是字符值。
NSString *string = @"0";
NSComparisonResult result = [string caseInsensitiveCompare:@"A"];
switch (result) {
case NSOrderedAscending:
NSLog(@"升幂");
break;
case NSOrderedSame:
NSLog(@"忽略大小写相同的字串");
break;
case NSOrderedDescending:
NSLog(@"降幂");
break;
default:
NSLog(@"无法判定");
break;
}
理解objc_msgSend的作用
在OC中,如果向某对象传递信息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数.
然而对象收到 消息后,究竟该调用哪个方法则完全于运行期决定,甚至可以在程序运行时改变,这些特性使得OC成为一门真正的动态语言。
在OC中,给对象发送消息的语法是:
id returnValue = [someObject messageName:parameter];
这里,someObject叫做“接收者(receiver)”,messageName:叫做"选择子(selector)",选择子和参数合起来称为“消息”。编译器看到此消息后,将其转换为一条标准的C语言函数调用,所调用的函数乃是消息传递机制中的核心函数叫做objc_msgSend,它的原型如下:
void objc_msgSend(id self, SEL cmd, ...)
第一个参数代表接收者,第二个参数代表选择子,后续参数就是消息中的那些参数,数量是可变的,所以这个函数就是参数个数可变的函数。
因此,上述以OC形式展现出来的函数就会转化成如下函数:
id returnValue = objc_msgSend(someObject,@selector(messageName:),parameter);
这个函数会在接收者所属的类中搜寻其“方法列表”,如果能找到与选择子名称相符的方法,就去实现代码,如果找不到就沿着继承体系继续向上查找。如果找到了就执行,如果最终还是找不到,就执行消息转发操作。
注意:如果匹配成功的话,这种匹配的结果会缓存在“快速映射表”里面。每个类都有这样一块缓存。所以如果将来再次向该类发送形同的消息,执行速度就会更快了。
==比较的是内存地址(像strong)isEqual比较的是内容(更像copy)
我们使用isMemberOfClass:能够判断出对象是否为某个特定类的实例;
而isKindOfClass:方法能够判断出对象是否为某类或其派生类的实例。
这两种方法都是利用了isa指针获取对象所属的类,然后通过super_class类在继承体系中查询。在OC语言中,必须使用这种查询类型信息的方法才能完全了解对象的真实类型。因为对象类型无法在编译期决定。