第十章 对象初始化
分配对象
两种方式:[[Object alloc] init]或者[Object new]
使用alloc init进行对象的创建比new要来的好 原因
alloc执行会将内存区域置为0
不能使用Object *obj = [obj alloc]; [obj init];这样的方式,因为init返回的对象可能和分配的对象不一样
初始化方法一般这样编写:
1 2 3 |
if(self = [super init]) ... return (self); |
便利初始化
NSString中的- (id) initWithFormat: (NSString) format, …; 可以用来代替init函数进行初始化
NSString中的- (id) initWithContentsOfFile: (NSString) path encoding: (NSStringEncoding) enc error: (NSError **) error;其中encoding表示编码类型,NSUTF8StringEncoding表示UTF-8编码,最后一个参数表示error,如果这个值不是nil的话就表示出错了,可以用localizedDescription查看情况
1 2 3 4 5 6 7 |
NSError *error = nil; NSStringEncoding encoding = NSUTF8StringEncoding; NSString *string = [[NSString alloc] initWithContentsOfFile: @"..." usedEncoding: &encoding error: &error]; if(nil != error) { NSLog(@"Unable to read data from file, %@", [error localizedDescription]; } |
这里的localizedDescription表示错误的本地化描述
一般来说最好的办法就是定义一个指定初始化函数,最好使用一个最多参数的函数作为指定初始化函数,其他所有的函数都基于这个指定初始化函数来写
那么子类只需要重写指定初始化函数就可以实现类的初始化
内存管理
在管理内存的时候,dealloc中一定要释放所有的内存,并在最后调用[super dealloc]
第十一章 属性
属性
1 2 3 4 5 6 7 8 |
@interface AllWeatherRadial: Tire { float rainHandling; float snowHandling; } @property float rainHandling; @property float snowHandling; @end // AllWeatherRadial |
@property表示定义出来的变量具有float属性
1 2 |
@synthesize rainHandling @synthesize snowHandling |
@synthesize会自动创建该属性的getter和setter,并且在synthesize的时候,编译器会自动创建与属性名称相同的变量,实例变量也可以在@implementation中用{}代码块(类似于@interface中的方法)来获得
在Objective-C2.0以上,可以使用类似于C++的点表达式来访问成员变量
1 2 3 4 |
[tire setRainHandling: 20 + i] tire.setRainHandling = 20 + i; @NSLog("%.f", [tire RainHandling]); @NSLog("%.f", tire.RainHandling); |
以上两组代码都是等价的
@property (copy) NSString *name;表示以后name会被复制,这样程序员就会知道自己无需复制文本框内的字符串
@property (retain) Engine *engine; 表示以后engine只有保留额释放特性,retain表示指针拷贝
要注意的是,我们如果没有setter和getter的话,我们是无法访问变量的
因为是非gc的对象,所以默认的assign修饰符是不行的。那么什么时候用assign、什么时候用retain和copy呢?推荐做法是NSString用copy,delegate用assign(且一定要用assign,不要问为什么,只管去用就是了,以后你会明白的),非objc数据类型,比如int,float等基本数据类型用assign(默认就是assign),而其它objc类型,比如NSArray,NSDate用retain。
如果两者都没有使用的话,默认会使用assign
在IOS程序中nonatomic可以提升访问速度,如果自定义了setter和getter的话,就不能使用atomic特性了,必须使用nonatomic
如果在Car类中要使用其他名称来调用实例变量,那么只需要在头文件中修改变量名称并在.m文件中使用@synthesize name = 变量名; init中要将name= @”Car” self.name = @”Car”
只读属性
@property (readwrite, copy) NSString *name;
@property (readwrite, retain) Engine *engine;
一般来说我们不用特意声明读写的属性,因为默认就是可读写的
如果需要只读,那么只需要用成
@property (readonly) float shoeSize;
类似这样的方法就可以了
这是如果调用- setShoeSize:方法就会报错
dynamic
@dynamic告诉编译器不要创建getter、setter和变量
例如:
1 2 3 4 5 6 |
@property (readonly) float bodyMassIndex; @dynamic bodyMassIndex; - (float) bodyMassIndex { //... } |
如果没有定义则会出现报错
@property (getter=isa) BOOL a;
这样的话getter就是isa了
要注意的是,我们只能重载getter和setter等,而不能重写别的函数
第十二章 类别(category)
@interface部分
1 2 3 |
@interface NSString (NumberConvenience) - (NSNumber *) lengthAsNumber; @end |
特点是括号中有一个新名称,意味着给他加了一个类别,只要类别名称唯一,你就可以像一个类中添加任意数量的类别,要注意的是,类别中不可以添加实例变量
概念:类名、类别名
@implementation部分
可以在这里实现自己的方法
概念:类名、类别名,方法的实现
1 2 3 4 5 6 |
@implementation NSString (NumberConvenience) - (NSNumber *) lengthAsNumber { NSUInteger length = [self length] return ([NSNumber numberWithUnsignedInt: length); } |
个人理解:类别差不多就是对类提供了一个操作的集合,可以理解为一个类的特殊子类,但是这个特殊子类里面不能包含实例变量
使用的时候使用如下代码:
1 2 3 4 5 6 7 8 9 10 |
#import "NSString+NumberConvenience.h" int main(int argc,const char *argv[]) { @autoreleasepool { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject: [@"hello" lengthAsNumber]forKey: @"hello"]; ... } } |
注意:类别方法和方法冲突的时候,类别方法拥有更高的优先级
类扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@interface Things : NSObject @property (assign) NSInteger thing1; @property (readonly, assign) NSInteger thing2; - (void) resetAllValues; @end @interface Things() { NSInteger thing4; } @property (readwrite, assign) NSInteger thing2; @property (assign) NSInteger thing3; @end // Things(); |
第二部分的代码中,重写了thing2,添加了方法thing3和实例变量thing4,这里面的代码都是私有属性和方法,上面的则是公有的,对于thing2来说,上面的方法是它的公共接口,下面的方法是它的私有接口。
注意:多个类扩展会出现可能难以察觉的Bug,且容易影响代码可读性
类别可以用来分散代码,就是说将一堆方法分散到逻辑群组中,使得程序员更容易阅读头文件
通过类别创建前向引用
原因:如果调用方法的时候未声明对象的某个方法的话,那么就会找不到该方法的声明和定义
所以我们可以声明一个方法,其中的常用策略就是将类别置于实现文件的最前面,实现的时候尽量还是在@implementation中实现,否则编译器仍然会报警告
非正式协议
interface这么声明
@interface A : NSObject <NSNetServiceBrowerDelegate>
告诉编译器A这个类符合这个协议
定义一个非正式协议只需要定义一个NSObject的一个类别即可,不需要实现这个类别,工程中不经常使用到
第十三章 协议
正式协议
正式协议采用@protocol关键字来实现的,并且和非正式协议不同的是,正式协议只能显式调用
可以继承父协议,与继承父类类似,使用尖括号表示继承
1 2 3 4 5 |
@protocol NSCopying - (id) copyWithZone: (NSZone *) zone; @end @protocol MySuperDuberProtocol <MyParentProtocol> @end |
协议名称必须要唯一
需要使用协议或多个协议,则使用以下语法:
1 2 3 4 5 6 7 8 9 10 11 12 |
@interface Car : NSObject <NSCopying> { // instance variables } //methods @end // Car @interface Car : NSObject <NSCopying, NSCoding> { // instance variables } //methods @end // Car |
NSZone指向一部分可分配的内存区域,使用的时候注意先使用Zone
1 2 3 4 5 6 7 8 |
- (id) copyWithZone: (NSZone *) zone; { AllWeatherRadial *tireCopy; tireCopy = [super cuperWithZone: zone]; tireCopy.rainHandling = rainHandling; tireCopy.snowHandling = snowHandling; return (tireCopy); } |
实例变量可以使用->运算符访问
运用某个协议的类,则需要实现这个协议的所有@required友元方法
@required表示必须实现的方法
@optional表示可选实现的方法
用协议修饰变量、函数:
1 2 |
id<MyProtocolName>myNewVariable; - (void) doSomethingWithThisObject: (id<MyProtocolName>) aObject |
变量myNewVariable和doSomethingWithThisObject: 的参数都需要遵循MyProtocolName协议
委托
目的:将A的事情委托给B来做
- 在A中声明协议,即要委托出去的事情(方法)。
- A中声明一个委托对象,即要委托给谁去办,用id<协议名字>委托对象名;
- 比如想让B做A的事情,那么B的interface中要宣布自己遵循A中制定的那个协议。
- A调用委托执行那件事情去.即[delegate fun]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
@protocol Mydelegate//协议的声明 -(void)Fun; @end @interface A:NSObject { id<Mydelegate> delegate;//声明一个委托对象,我们要把事情委托给实现Mydelegate协议的类 } @property(assign,nonatomic) id<Mydelegate> delegate; @end @implementation -(void)init { delegate = B; } -(void)FunToOther { [delegate Fun];//将消息发送给委托去处理 } @end @interface B:NSObject<MyDelegate> @end @implementation -(void)Fun { //实现Mydelegate中制定的方法 } @end |
第十四章 代码块
代码块
和老式的函数指针对应
个人感觉:和lambda匿名函数差不多
特性如下:
- 返回类型可以手动声明也可以自动推导;
- 具有指定类型的参数列表
- 拥有名称
正常的函数指针:
1 |
void (*my_func)(void); |
将这个函数指针的*更换为^就是代码块了
1 |
void (^my_block)(void); |
一般来说可以用以下的关系来表示他们:
1 |
<returntype> (^blockname) (list of arguments) = ^(arguments){ body; }; |
函数方法使用代码块
我们可以通过直接调用blockname来像使用函数一样使用这个代码块
代码块可以访问与它相同的(本地)有效范围内声明的变量,也就是说代码块可以访问与它同时创建的有效变量,例如在定义代码块前定义一个变量,则代码块可以使用这个变量
直接使用代码块
1 2 3 |
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(NSString *object1, NSString *object2){ return [object1 compare: object2] }]; |
使用typedef关键字
1 2 3 |
typedef double (^MKSampleMultiply2BlockRef)(double c, double d); MKSampleMultiply2BlockRef multiply2 = ^(double c, double d){return c * d;};//代码块定义 multiply2(a, b)//调用 |
注意:代码块在定义的时候会保存在定义的时候所有本地变量的状态,即使在后面更改了变量的值,也不会影响代码块运行的结果,要改变这种状态的话,需要使用static关键字来创建全局变量,对于全局变量代码块是能看到的
参数变量
参数变量和函数中的参数变量类似
__block变量
这个变量类型表示该变量可以被在它作用域以内的代码块修改,类似这样定义__block double c = 3;
代码块内部的本地变量
该变量类型和本地变量的类型差不多
OC变量
- 如果引用了一个OC对象,必须要保留它
- 如果通过引用访问了一个实例变量,要保留一次self(即执行方法的对象)
- 如果通过数值访问了一个实例变量,变量需要保留
参考书上P223,很详细
0 条评论