多用字面量语法 例如定义字符串
NSString *str=@"something is stranger";
NSNumber *intNumber = @1;
NSNumber *floatNumber = @1.3f;
NSNumber *booleanNumber = @YES;
NSNumber *charNumber = @'test';
NSString *str = [arry objectAtIndex:1];
应用字面NSString *str = arry[1];面量语法创造出来的对象都是不可变的,若想创建可变的对象,需要复制一份
NSMutableArry *mutable = [@[@1,@2,@3,@4]mutalbeCopy];
如果不想令编译器自动合成存取方法,可以自己实现,利用@dynamic关键字,她会告诉编译器,不要自动创建实现属性所用的实例变量,也不要为其创建存取方法。
strong和retain的意思差不多。都是保存新值,释放旧值,然后把新值赋值给旧值。strong 表示了一种拥有关系
weak 表示了一种非拥有关系 为这种属性设置新值是,设置的方法既不保留新值,也不释放旧值,当属性所指的对象遭到摧毁的时候,属性值也会清空。
Copy 此特性的值和strong类似。然而设置方法是不保留新值,而是将其拷贝给旧值。
下面的代码解释了strong和copy 例如在一个类中定义了两个属性,一个属性的类型是NSString,一个是UIView
@interface TestCharacter:NSObject {
NSString *name;
UIView *textView;
}
- (void)setName:(NSString*)newName;
- (NSString*)name;
- (void)setTextView:(UIView*)newTextView;
- (UIView*)textView;
@end
下面是这个类的实现文件
#import "test.h"
@implementation TestCharacter
- (void)setName:(NSString*)newName {
[name release];
name = [newName copy];
}
- (NSString*)name {
return name;
}
- (void)setTextView:(UIView*)newTextView {
[newTextView retain];//保留新值
[textView release];//释放旧值
textView = newTextView;//赋值
}
上面这段代码就和下面的代码是等价的
@property (copy) NSString *name;
@propery (strong) UIView *textView;
这句话的意思就是:不要使用点语法,点语法是通过存取方法来访问相关实例变量的。
就是不要用self.xxx,要用下划线xxx _xxx
属性其实代表了两部分:一部分是存取方法,一部分是实例变量。默认的实例变量的名称是下划线+属性的名称。如果不想用这个名称,可以使用@synthesize方法重新定义。
如果使用了懒加载技术,必须使用“获取方法”来调用属性,否则属性永远不会实例化。
在初始化及delloc中,总是应该直接通过实例变量来读写数据
“isMemberOfClass:”能够判断出对象是否为某个特定类的实例。而"isKindOfClass:"能够判断出对象是否为某类或起派生类的实例
NSMutableDictionary *dict = [NSMutableDictionary new];
[dict isMemberOfClass:[NSDictionary class]]; /// < NO
[dict isMembeOfClss:[NSMutableDictionary class]];/// < YES
[dict isKindOfClass:[NSDictionary class]];///
因为OC没有命名空间,所以很容易发生命名冲突。所以可以为你定义的类和对象、函数的名称加上适当的前缀。由于Apple宣称其保留使用“两个字母前缀”的权利,所以个人使用的时候前缀至少从3个字母开始。
在这里还提到当你编写的程序库用到第三库的时候,应该为第三库都加上你自己的前缀,我觉得这条就比较扯淡了,第三方库的类和函数可能很多,怎么可能加完呢?例如SDWebImage,那么多库,怎么可能。我觉得比较好的是用 cocoapods,告诉使用者自己的库依赖了那些库,让使用者自己去下载,而且也避免了版本号的更新
使用这个方法可以很方便的打印并查看信息
小技巧:在description中能输出很多互不相同的信息,可以借助NSDictionary类的description方法来实现。
NSObject协议中还有个方法:debugDescription,此方法的用意和description很类型。二者的区别在于, debugDescription方法是开发者在调试器中以控制台命令打印对象时才调用
在头文件中定义的对象为只读的,如果想在实现中进行修改,可以使用分类操作,重新对这个对象进行声明,声明为可读写的。
虽然在 oc 中可以定义的属性为只读的,但是仍然可以使用KVC来对可读的属性进行设置。另外一种方法就是使用 runtime 机制进行修改。
Objective-c的方法和变量名使用了“驼峰式大小写命名法”–以小写字母开头,其后每个单词首字母大写。类名也用驼峰命名法,不过其首字母要大写,而且前面通常还有两三个前缀字母
这个是为了更好的区分公共方法和私有方法。
要点:
通常不要抛出异常,原因是因为会导致内存泄漏。
对非致命错误,通常的做法是令方法返回nil/0 ,或者是使用NSError,以表明其中有错误发生。
NSError 是用来处理运行时错误的,这种错误在运行的时候随时可能出现,而且是可以处理的,是可以显示给用户的。
而异常处理是用来处理编程错误的,例如数组越界,堆栈溢出等错误,这些错误比较严重,一般都会导致程序崩溃和终止。
要点
在使用对象是如果需要拷贝,在Objective-C中,此操作是通过copy方法完成的。如果想令自己的类支持拷贝操作,就要实现NSCopying协议,该协议只有一个方法
-(id)copyWithZone:(NSZone*)zone;
为什么会出现NZone?因为以前开发程序的时候,会根据此把内存分成不同的“区”(zone),而对象会创建在某个区里面。现在不用了,每个程序只有一个区:默认区(default zone)。所以说,实现这个方法的时候不用担心Zone这个参数,已经不用了。
NSArray *arry <= [NSMutableArry copy] 返回的是一个不可变类
NSMutable *mutableArry <= [NSMutableArry mutableCopy]返回一个可变类
在Foundation框架中所有的collection类在默认情况下都是执行的浅拷贝
在定义代理属性的时候,属性需要定义为weak,而非strong,因为委托协议的对象和使用它的对象之间并非拥有的关系。
@propery delegate;
为了指明可选方法,委托协议经常使用@optional关键字来标准起大部分或者全部的方法
如果要在委托对象上调用可选方法,那么必须提前使用类型信息查询方法来判断这个委托对象是否能相应相关选择子。
if ([_delegate responsToSelector:@selector(xxxDelegate)]){
//handle code
}
如果要频繁的判断某个委托对象是否能响应特定的选择子,会导致效率比较低。对应的处理方式就是事先把查询的结果缓存起来,每次使用的时候,查询一下结果就行了,不需要每次都去判断。这本书里提供的方法就是使用结构体的位域来实现。
Category 有的书上也翻译为 类别
分类一是为了方便类的管理
二是便于在扩展类的方法的时候,可以不用继承来实现,非常的方便。只是在分类中不能够添加属性
遗留问题:runtime 机制是如何实现分类的?
这个其实就是在模拟命名空间,防止覆盖主类的方法。
属性只能定义在“主接口”中,分类中不能定义。分类中如果想定义,可以使用关联对象来实现
类所封装的全部数据应该定义在主接口中,这里也是唯一能够定义实例变量的地方。而属性只是定义实例变量及相关存取方法的所用的“语法糖”。分类则应将其理解为一种手段,目标在于扩展类的功能,而非封装数据
在分类中是可以添加只读属性的,如果只读属性只有一个 get 方法,并且不需要访问数据,并且属性也不需要实例变量来实现,那么可以添加属性到实例变量
分类中之所以没办法添加属性,是因为编译器没办法给分类的属性生成存取方法.
在 Xcode10.1中如果分类中有属性,但是不使用属性,不给属性赋值,只会警告,运行不会出错。但是如果在程序中对属性进行了赋值,运行的时候就会出错
class-continuation 翻译为 类扩展 或者叫做 匿名类
1可以在类扩展中添加实例变量 和 方法
2.如果某个属性在主接口中声明为只读,如果想在主方法中进行读写,可在类扩展中修改这个属性为可读写。但是这样会出现“竞争”问题:外部在进行读,而内部正在进行写,这样就需要多属性的写进行加锁。
3.协议也可以在类扩展中进行声明
在 ARC 环境下,这个函数是由运行期系统来决定的,然后具体何时执行,则无法保证。程序自己不能调用 delloc 方法,只能决定在 delloc 方法中执行那些动作。
在 delloc 中主要是用来释放对象所拥有的引用
僵尸对象是Cocoa提供的一个非常方便的调试功能。启用这项调试功能之后,运行期系统会把所有已经回收的实例转换成特殊的“僵尸对象”,而不会真正的回收它们。这种对象所在的核心内存无法重用,因此不可能遭到覆写。僵尸对象收到消息后,会抛出异常。其中准确的说明了发送过来的消息,并描述了回收之前的那个对象。这样就可以看成是哪里出了问题。
僵尸对象是在运行期生成的,当碰到的对象要变成僵尸对象的时候,就会创建一个这样的类。
僵尸类是从名为 NSZombie的模板类里复制出来的。这些僵尸类没有什么事情可做,只是从当一个标记。一般僵尸类的名称都是NSZombie+类名 利用这个信息就可以知道是哪个类对象出了问题
很多文章对官方解释做了详尽的翻译(例如可以参考Dispatch_barrier_async的研究), 而我更习惯于提炼其中的几个要点如下
通过dispatch_barrier_async添加的block会等到之前添加所有的block执行完毕再执行
在dispatch_barrier_async之后添加的block会等到dispatch_barrier_async添加的block执行完毕再执行
dispatch_barrier_async的上述特点只在自己创建的concurrent queue有效, 在serial queue和global concurrent queues中的作用和dispatch_sync完全相同