目录:
- 在字符串查看指定字符串
- UILabel自适应
- 服务器数据处理
- copy解释
- 对象及可变字典赋值取值方法
- nil NSNULL NULL解释
- 字面量
- UILabel 黑线问题
- nullable和nonnull
1. 在Objective-C
中怎么检查一个字符串中是否还有另外一个字符串.
- iOS8或
OS X Yosemite
之后:
- (BOOL)containsString:(NSString *)str NS_AVAILABLE(10_10, 8_0);
之前:
NSString *string = @"hello bla bla";
if ([string rangeOfString:@"bla"].location == NSNotFound)
{
NSLog(@"string does not contain bla");
} else {
NSLog(@"string contains bla!");
}
2. UILabel自适应
-
UILabel
不能设置竖直方向的排列布局,可以通过sizeToFit
改变label
的frame
来实现曲线救国。 - 适用于根据字体计算出文本单行的长度和高度(宽度和高度),注意是单行,首先来看单行文本的问题:对于单行文本来说,计算
CGSize
就比较简单了
CGSize size = [s.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:10]}];
- 多行文本的显示:
首先UILabel
的numberOfLines
设置为0
,其次通过方法来计算CGSize
,具体代码如下:button.titleLabel
亦如此
NSDictionary *attribute = @{NSFontAttributeName: font};
height = [text boundingRectWithSize:CGSizeMake(width, 0) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading | NSStringDrawingTruncatesLastVisibleLine attributes:attribute context:nil].size.height;
- UITextView多行文本的显示:
-
boundingRectWithSize:options:attributes
来计算。
嗯确实,这是个利器。其本上能正确返回字体的rect。但对于UITextView 似乎使用此方法计算出来的结果比实际显示的要小:UITextView
在上下左右分别有一个8px的padding
,需要将UITextView.contentSize.width
减去16像素(左右的padding 2x8px)。同时返回的高度中再加上16像素(上下的padding),这样得到的才是UITextView真正适应内容的高度。如代码中
-
CGSizeMake(width -16.0, CGFLOAT_MAX)
return sizeToFit.height + 16.0。
UILable中则不用.
- 通用(推荐)
CGSize sizeToFit = [textView sizeThatFits:CGSizeMake(width, MAXFLOAT)];
3.后台数据处理
- 系统请求方法
接受到的数据类型为NSData
类型,需要json解析
,解析后整个json字符串
为字典类型,里面的数据类型可以为数组
,字典
,NSNumber
(一般数据类型
?BOOL
),字符串
。
NSDictionary *responsObj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
- AFN请求方法
通过指定接受到的数据格式为text/json
,可自动将数据解析 - ** 通过以上两种方法获取数据并解析后。要对数据做容错处理。**
通常我们以约定好的数据类型,定义对象去接受并使用数据,但要注意,有时候后台返回的并不是我们在客户端定义的数据类型,所以以下要做容错处理。
由于数据造成app crash
原因包括:获取的对象跟我们使用的方法不一致,比如字符串对象调用了字典的方法,空对象可以调用任何方法,这是系统设定的,不会crash
。
所以一般判断步骤为:首先以约定好的数据类型接受后台数据,然后判断对象是否为空(nil,NULL)
,再判断是什么类型,特别是嵌套包含的数据,要一步步判断,切勿偷懒直接用字面量方法获取。
4. 三种方式 copy, mutableCopy, =
copy
mutableCopy
统称为copy
共同特点:对原对象改变,副本不改变;对副本改变,原对象不改变,互不影响。
作用:在改变原有对象的时候,不会改变新对象的值
原因在本小段末尾解释.copy :
创建的对象是不可变副本(如NSString
、NSArray
、NSDictionary
)。
不可变对象调用copy
,原对象与副本对象地址一样,没有生成新对象,是浅拷贝;
可变对象调用copy
,原对象与副本对象地址不一样,生成了新的对象,是深拷贝。mutableCopy:
创建的对象是可变副本也是新对象(如NSMutableString
、NSMutableArray
、NSMutableDictionary
)。
不论是可变不可变对象调用mutableCopy方法
,都属于深拷贝,生成一个新的对象,地址不一样。=:
同一个对象,改变任何一个,它俩都随之改变.-
解释改变其中一个另一个却不改变的原因:
当对象为可变的对象,调用copy
或者mutableCopy
都会生成新的对象;
当对象为不可变的时候,使用copy
,不能对copy
后的对象进行操作;使用mutableCopy
,生成了新的对象.
5. setvalue 与set object 字面量
-
setObject:ForKey:
是NSMutableDictionary特有的;setValue:ForKey:是KVC的主要方法; -
setObject:ForKey:
中object
对象不能为nil
,不然会报错;key
的参数只要是对象就可以,一般是NSString
; -
setValue:ForKey:
中Value
值可以为nil
,此时会自动调用removeObject:forKey:
方法;key
的参数只能是NSString
类型; -
setValue:ForKey:
是在NSObject
对象中创建的,即所有的对象都有这个方法,可以用于任何类(方法调用者是对象的时候); - 注意:
-
[imageDictionary setObject:[NSNullnull] forKey:indexNumber];
[NSNull null
]表示的是一个空对象,并不是nil, - 首先不可变字典可以调起
setValue:forKey:
,但不能真正的进行操作,这取决与不可变字典不可增删改的特性。
-
-
setValue:forKey:
与setValue:forKeyPath:
- 动态设置:
setValue:属性值 forKey:属性名
(用于简单路径)、setValue:属性值 forKeyPath:
属性路径(用于复合路径,例如Person有一个Account类型的属性,那么person.account
就是一个复合属性) - 动态读取:
valueForKey:属性名
、valueForKeyPath:属性名
(用于复合路径)
Amodel *modelA = [[Amodel alloc]init];
SubAmodel *modela = [[SubAmodel alloc]init];
modela.str = @"qq.com";
modelA.submodel = modela;
NSLog(@"%@",modelA.submodel.str);
[modelA setValue:@"QQ.com" forKeyPath:@"submodel.str"];
NSLog(@"%@",modelA.submodel.str);
- 建议在
NSDictionary
下只用objectForKey:
来取值。
6. NSNull NULL nil
- nil:指向oc中对象的空指针,针对对象。指针为空,不会发送消息的;
return NO
Nil:指向oc中类的空指针,针对类。
NULL:指向其他类型的空指针,如一个c类型的内存指针,基本数据类型为空,基本类型。
null,占位空对象,即野指针,指向垃圾内存,造成crash
。抛出异常NSException
NSNull:类名,它的实例就是null,空值对象。
NSArray *arr1 = [NSNull null];
NSArray * arr2 = nil;
NSLog(@"%@", arr1);
NSLog(@"%@", arr2); (null)
NSLog(@"%p", arr1); 0x10cf4bd80
NSLog(@"%p", arr2); 0x0
if ([arr1 isKindOfClass:[NSArray class]]) {
}
if (arr1 == NULL) {
}
if (arr1 == nil) {
}
if ([arr1 isKindOfClass:[NSNull class]]) {
//arr1 只走这个+1
}
if ([arr2 isKindOfClass:[NSArray class]]) {
}
if (arr2 == NULL) {
//arr2 走这个+1
}
if (arr2 == nil) {
//arr2 走这个+2
}
if ([arr2 isKindOfClass:[NSNull class]]) {
}
[arr1 count]; //会crash。
[arr2 count];//不会crash。
-
僵尸对象
:被释放的对象为僵尸对象, 已经被销毁的对象(不能再使用的对象).
野指针
: 指向僵尸对象(不可用内存
)的指针 给野指针发消息会报EXC_BAD_ACCESS
错误
空指针
: 没有指向存储空间的指针(里面存的是nil
, 也就是0
) 给空指针发消息是没有任何反应的,空指针是把指针为nil
.
为了避免野指针错误的常见办法: 在对象被销毁之后, 将指向对象的指针变为空指针
接受null
数据并打印并不会造成crash
,造成crash
的原因是发送消息 - 服务器返回的数据,有
null
,还有nil
,iOS
是使用的OC
语言, 跟C语言略有不同, 在OC中打印出(nill)
和
两种情况是不同的,
的判断方法是
- (id) setNoNull:(id)aValue{
if (aValue == nil) {
aValue = @"";//为null时,直接赋空
} else if ((NSNull *)aValue == [NSNull null]) {
aValue = @"";
if ([aValue isEqual:nil]) {
aValue = @"";
}
}
return aValue;
}
既然它老出bug ,为什么还在oc里还有它的存在
NSNull
与nil
以及NULL
不同,因为它是一个实际的对象,而不是一个零值。
nil
null
其实就是0
NSNull
在Foundation
和其它框架中被广泛的使用,以解决如NSArray
和NSDictionary
之类的集合不能有nil
值的缺陷。
你可以将NSNull
理解为有效的将NULL
或者nil
值封装boxing,以达到在集合中使用它们的目的:
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
mutableDictionary[@"someKey"] = [NSNull null]; // Sets value of NSNull singleton for `someKey`
NSLog(@"Keys: %@", [mutableDictionary allKeys]); // @[@"someKey"]
7. 字面量
- 字面数值
需要把整数、浮点数、布尔值封入到对象里。通常情况下会用到如下方法:
NSNumber *number = [NSNumber numberWithInt:8];
使用字面量语法后,不仅语法更简洁,还有很多好处。
NSNumber *number = @(8);
能够用以NSNumber实例表示的所有数据类型,都可以使用字面量语法。。。 - 字面量数组
数组的常用创建方法如下:
NSArray *array = [NSArray arrayWithObjects:@"obj1", @"obj2", nil];
而使用字面量语法则是:
NSArray *array = @[@"obj1", @"obj2"];
数组的取下标也有字面量语法:
NSString *obj = [array objectAtIndex:1];
使用字面量:
NSString *obj = array[1];
不过使用字面量数组时,要注意不要把nil加入到数组中,否则会抛出异常。
字面量字典
常用创建方式如下:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"obj1", @"value1", @"obj2", @"value2", nil];
而使用字面量语法,就比较简洁了。
NSDictionary *dictionary = @{@"obj1": @"value1",
@"obj1": @"value1"};
字面量语法清晰表示出了,key和value的一一对应关系。但是与数组一样,字面量字典的value不能为nil,否则会出现异常。字面量可变数组与字典
对于可变的数组与字典,同样可以使用自变量语法对自变量数组,字典进行操作。
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:@{@"obj1": @"value1", @"obj1": @"value1"}];mutableDictionary[@"obj3"] = @"value3";** 注意用字面量语法创建数组及字典时要注意,若数组元素对象中有nil,则会抛出异常**
使用字面量语法创建出来的字符串、数组、字典对象都是不可变的(immutable
)。若想要可变版本的对象,则需复制一份:
NSMutableArray *mutableArray = [@[@1, @2, @3, @4, @5]mutableCopy];
mutableArray[3] = @33;
NSMutableDictionary* mutableDic = [@{@"name":@"song",
@"age":@"28",
@"tel":@"1234567"}mutableCopy];
mutableDic[@"age"]=@"30";
- 总结
-
Foundation
框架 是在iOS开发
中 用到的最频繁的基础框架,它提供了几个最基本的类:NSString
、NSNumber
、NSArray
、NSDictionary
. 在这个框架下,尽量使用对象字面量语法创建字符串, 数字, 数组和字典等.其他自定义的对象不可使用字面量。 - 在数组和字典中, 要使用关键字和索引做下标来获取数据
- 使用对象字面量语法时, 容器类里不可是nil, 否则运行时贵抛出异常.
8. UILabel 黑线问题
偶尔发现UILabel右边缘出现黑线,iPhone6P、6sP最为明显:
使用循环计算label尺寸, 循环创建label时有可能出现右边缘黑线的问题, 且有时在iPhone5s一下机型不会出现, 只在iPhone6以上出现 ,这是因为计算出得size可能的值会是30.31123323…… 这样的数。
猜想: 而像素值显示的时候不可能出现显示半个像素的情况, 那么不足一个像素的值就会被忽略掉, 在分辨率较低的机型上不会出现, 而分辨率较高的则不会忽略, 就出现了黑线。
解决方法
计算出来的UILabel尺寸,向上取整
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping ;
[paragraphStyle setLineSpacing:4];
NSDictionary *attributes = @{NSFontAttributeName:kDesFont, NSParagraphStyleAttributeName:paragraphStyle.copy};
CGSize size = [text boundingRectWithSize:CGSizeMake(cellWidth, 0) options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attributes context:nil ].size ;
size.width = ceil(size.width);
size.height = ceil(height);
9. nullable和nonnull
Xcode 6.3新特性:Nullability Annotations,这一新特性的核心是两个新的类型注释:nullable和nonnull。从字面上我们可以猜到,nullable表示对象可以是NULL或nil,而nonnull表示对象不应该为空。当我们不遵循这一规则时,编译器就会给出警告。
事实上,在任何可以使用const关键字的地方都可以使用nullable和nonnull,不过这两个关键字仅限于使用在指针类型上。而在方法的声明中,我们还可以使用不带下划线的nullable和nonnull,如下所示:
- (nullable id)itemWithName:(NSString * nonnull)name
在属性声明中,也增加了两个相应的特性,因此上例中的items属性可以如下声明:
@property (nonatomic, copy, nonnull) NSArray * items;
也可以用以下这种方式:
@property (nonatomic, copy) NSArray * __nonnull items;
如果需要每个属性或每个方法都去指定nonnull和nullable,是一件非常繁琐的事。苹果为了减轻我们的工作量,专门提供了两个宏:NS_ASSUME_NONNULL_BEGIN,NS_ASSUME_NONNULL_END。在这两个宏之间的代码,所有简单指针对象都被假定为nonnull,因此我们只需要去指定那些nullable的指针。
NS_ASSUME_NONNULL_BEGIN
@interface TestNullabilityClass ()
@property (nonatomic, copy) NSArray * items;
- (id)itemWithName:(nullable NSString *)name;
@end
NS_ASSUME_NONNULL_END