23、Objective - C 快速指南:初始化器、内存管理、协议与代理解析

Objective - C 快速指南:初始化器、内存管理、协议与代理解析

1. 初始化器

在Objective - C中,初始化器是用于初始化对象的特殊方法。以 initWithName:andRate: 初始化器为例,其实现如下:

- (id)initWithName:(NSString *) n andRate:(float) r
{
    if (self = [super init]) {
       //...
       //...
    }
    return self;
}

在这个初始化器中,首先调用了超类的 init 方法,确保基类被正确初始化,这是初始化当前类的必要步骤。定义初始化器的规则很简单:如果类被正确初始化,应该返回 self 的引用(因此返回类型为 id );如果失败,则返回 nil

对于 initWithName: 初始化器,它调用了 initWithName:andRate: 初始化器:

- (id)initWithName:(NSString *) n
{
     return [self initWithName:n andRate:0.0f]; 
}

一般来说,如果有多个不同参数的初始化器,应该将它们链接起来,确保它们都调用一个执行超类 init 初始化器调用的初始化器,这个初始化器被称为指定初始化器。通常,指定初始化器应该是参数最多的那个。

可以在实例化时调用这些初始化器:

SomeClass *sc1 = [[SomeClass alloc] initWithName:@"Wei - Meng Lee" andRate:35];
SomeClass *sc2 = [[SomeClass alloc] initWithName:@"Wei - Meng Lee"];
2. 内存管理

在Objective - C编程(尤其是iPhone开发)中,内存管理是每个开发者都需要重视的重要话题。虽然Objective - C支持垃圾回收,但由于iPhone实现垃圾回收的开销过大,所以不支持垃圾回收,开发者需要手动分配和释放不再需要的对象内存。

2.1 引用计数

iPhone OS使用引用计数来跟踪对象,以确定它们是否仍被需要或可以被释放。每个对象都有一个计数器,对象创建时计数加1,对象释放时计数减1,当计数为0时,操作系统会回收该对象的内存。

以下是与内存管理相关的重要关键字:
- alloc :用于为创建的对象分配内存,例如:

NSString *str = [[NSString alloc] initWithString:@"Hello"];

在这个例子中,创建了一个 NSString 对象并初始化为默认字符串,对象创建时引用计数为1,创建者需要在使用完后释放其内存。

  • new :功能上等同于 [[Class alloc] init] ,例如:
NSString *str = [NSString new];

使用 new 关键字创建的对象,创建者同样需要在使用完后释放其内存。

  • retain :用于将对象的引用计数加1。例如:
NSString *str = [[NSString alloc] initWithString:@"Hello"];
NSString *str2 = str;
[str2 retain];
[str release];

在这个例子中, str2 原本不拥有对象,使用 retain 后引用计数变为2,即使 str 被释放, str2 仍然有效,使用完 str2 后需要手动释放。

  • release :用于释放对象,使对象的引用计数减1,当引用计数为0时,对象的内存被释放。例如:
NSString *str = [[NSString alloc] initWithString:@"Hello"];
//...do what you want with the object...
[str release];

需要注意的是,不能释放不拥有的对象,否则会导致运行时错误。但如果使用 retain 获得了对象的所有权,就需要使用 release 释放它。

在设置属性时,为了避免内存泄漏,需要正确使用 retain release

-(void) setName:(NSString *) value {
    [value retain];
    [name release];
    name = value;
}
2.2 便利方法和自动释放

使用便利方法(如 [NSString stringWithFormat:@"%d", 4] )创建的对象是自动释放对象,创建者不拥有这些对象,不能手动释放它们。这些对象会被添加到自动释放池中,当前方法退出时,池中的对象会被释放。例如:

NSString *str = [NSString stringWithFormat:@"%d", 4];

如果想拥有使用便利方法创建的对象,可以使用 retain 关键字:

NSString *str2 = [[NSString stringWithFormat:@"%d", 4]  retain ];

释放对象时,可以使用 autorelease release 关键字。 release 会立即减少引用计数,而 autorelease 会在稍后某个时间减少引用计数。例如:

NSString *str = [[NSString stringWithFormat:@"%d", 4] retain];
[str autorelease];  //you don't own it anymore; still available
NSLog(str);         //still accessible for now

在使用自动释放对象时需要注意,例如在循环中大量创建自动释放对象可能会导致内存不足,此时可以使用自动释放池来解决:

for (int i=0; i <=99999; i++){
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    NSString *str1 = [NSString stringWithFormat:@"%d", i];
    NSString *str2 = [NSString stringWithFormat:@"%d", i];
    NSString *str3 = [NSString stringWithFormat:@"%d", i];
    //...
    //...
    [pool release]; 
}
2.3 dealloc方法

使用 alloc new 关键字创建的对象,应该在使用完后尽快释放。对于属性,通常在 dealloc 方法中释放。例如:

@property (retain, nonatomic) NSString *name;
-(void) dealloc {
    [self.name release];   //---release the name property---
    [super dealloc];
}

当对象的引用计数为0时, dealloc 方法会被调用。例如:

SomeClass *sc1 = [[SomeClass alloc] initWithName:@"Wei - Meng Lee" andRate:35];
//...do something here … 
[sc1 release];  //---reference count goes to 0; dealloc will be called---
2.4 内存管理技巧

在iPhone编程中,内存管理是一个棘手的问题。可以通过以下方法检测可能影响应用程序的内存问题:
- 实现 didReceiveMemoryWarning 方法:

- (void)didReceiveMemoryWarning {  
    //---insert code here to free unused objects---
    [super didReceiveMemoryWarning];  
}

当iPhone内存不足时,该方法会被调用,应该在其中释放不需要的资源和对象。
- 处理 applicationDidReceiveMemoryWarning: 方法:

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application 
{  
    //---insert code here to free unused objects---
    [[ImageCache sharedImageCache] removeAllImagesInMemory];  
}

在这个方法中,应该停止所有内存密集型活动,如音频和视频播放,并移除所有缓存的图像。

3. 协议

在Objective - C中,协议是声明任何类都可以选择实现的编程接口。协议声明了一组方法,采用该协议的类可以选择实现其中的一个或多个方法。定义协议的类期望调用采用类实现的协议方法。

UIAlertView 类为例,创建并显示一个警告视图:

UIAlertView *alert = [[UIAlertView alloc]
                         initWithTitle:@"Hello"
                         message:@"This is an alert view"
                         delegate:self
                         cancelButtonTitle:@"OK"
                         otherButtonTitles:nil];
[alert show];

如果要显示更多按钮,可以设置 otherButtonTitles: 参数:

UIAlertView *alert = [[UIAlertView alloc]
                         initWithTitle:@"Hello"
                         message:@"This is an alert view"
                         delegate:self
                         cancelButtonTitle:@"OK"
                         otherButtonTitles:@"Option 1", @"Option 2", nil];

要知道用户点击了哪个按钮,可以处理 UIAlertViewDelegate 协议定义的方法:

alertView:clickedButtonAtIndex:   
willPresentAlertView:   
didPresentAlertView:   
alertView:willDismissWithButtonIndex:   
alertView:didDismissWithButtonIndex:   
alertViewCancel:

如果要实现这些方法,需要确保类(如视图控制器)符合该协议:

@interface UsingViewsViewController : UIViewController 
< UIAlertViewDelegate >   {  //---this class conforms to the UIAlertViewDelegate
                         // protocol---
    //...
}

然后在类中实现相应的方法:

- (void)alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex {

     NSLog([NSString stringWithFormat:@"%d", buttonIndex]); 

}
4. 代理

在Objective - C中,代理是被另一个对象指定负责处理事件的对象。在 UIAlertView 的例子中, UIAlertView 类的初始化器包含一个 delegate 参数。将其设置为 self 表示当前对象负责处理该 UIAlertView 实例触发的所有事件;如果不需要处理事件,可以将其设置为 nil

UIAlertView *alert = [[UIAlertView alloc]
                         initWithTitle:@"Hello"
                         message:@"This is an alert view"
                         delegate:self 
                         cancelButtonTitle:@"OK"
                         otherButtonTitles:nil];

UIAlertView *alert = [[UIAlertView alloc]
                         initWithTitle:@"Hello"
                         message:@"This is an alert view"
                         delegate:nil 
                         cancelButtonTitle:@"OK"
                         otherButtonTitles:nil];

以下是内存管理关键字的总结表格:
| 关键字 | 作用 |
| ---- | ---- |
| alloc | 为对象分配内存,创建者拥有对象,需手动释放 |
| new | 功能等同于 [[Class alloc] init] ,创建者拥有对象,需手动释放 |
| retain | 增加对象引用计数,使对象在其他引用释放时仍有效 |
| release | 减少对象引用计数,计数为0时释放对象内存 |
| autorelease | 承诺稍后减少对象引用计数,当前方法退出时释放对象 |

下面是一个简单的mermaid流程图,展示对象创建和释放的过程:

graph LR
    A[创建对象] --> B{引用计数 > 0?}
    B -- 是 --> C[继续使用]
    C --> D[释放对象]
    D --> B
    B -- 否 --> E[内存回收]

通过以上内容,我们对Objective - C的初始化器、内存管理、协议和代理有了更深入的了解,这些知识对于开发高质量的iPhone应用程序至关重要。

5. 引用计数的类比理解

为了更好地理解引用计数的概念,我们可以用一个图书馆房间的类比来解释。

想象图书馆有一个可以预订用于学习的房间。最初,房间是空的,所以灯是关着的。当你预订这个房间时,图书管理员会增加一个计数器,以表示使用该房间的人数。这类似于使用 alloc 关键字创建一个对象。

当你离开房间时,图书管理员会减少计数器的值。如果计数器此时变为0,这意味着房间不再被使用,灯就可以关掉了。这类似于使用 release 关键字释放一个对象。

有时候,你预订了房间并且是房间里唯一的人(此时计数器为1),这时你的一个朋友来了。他可能只是来拜访你,所以没有向图书管理员登记,因此计数器不会增加。因为他只是拜访你,没有预订房间,所以他没有权利决定是否关灯。这类似于在不使用 alloc 关键字的情况下将一个对象赋值给另一个变量。在这种情况下,如果你离开房间(释放对象),灯就会关掉,你的朋友也必须离开。

再考虑另一种情况,你正在使用房间,另一个人也预订了这个房间并和你一起使用。这时,计数器的值变为2。如果你离开房间,计数器会减到1,但灯仍然亮着,因为还有另一个人在房间里。这种情况类似于创建一个对象并将其赋值给另一个使用 retain 关键字的变量。在这种情况下,只有当两个对象都释放它时,对象才会被释放。

以下是这个类比与引用计数操作的对应关系表格:
| 图书馆场景 | 引用计数操作 |
| ---- | ---- |
| 预订房间,计数器加1 | 使用 alloc new 创建对象,引用计数加1 |
| 离开房间,计数器减1 | 使用 release 释放对象,引用计数减1 |
| 朋友拜访,计数器不变 | 不使用 alloc 赋值对象,不拥有对象 |
| 另一个人预订并共享房间,计数器加1 | 使用 retain 增加对象引用计数 |

6. 自动释放对象的行为类比

继续我们图书馆房间的类比,想象你要和图书管理员签退,这时你意识到自己把书落在房间里了。你告诉图书管理员你已经用完房间,现在要签退,但因为你把书落在房间里了,你让管理员先别关灯,这样你可以回去取书。之后,管理员可以在他方便的时候关灯。这就是自动释放对象的行为。

自动释放对象在当前方法退出时会被释放,就像你签退后,管理员会在合适的时候关灯一样。例如:

NSString *str = [NSString stringWithFormat:@"%d", 4];

这里创建的 str 是自动释放对象,你不拥有它,不能手动释放。当当前方法退出时,它会被自动释放。

7. 内存管理中的注意事项

在进行内存管理时,还有一些其他的注意事项需要牢记:

7.1 避免重复释放

不要对已经释放的对象再次调用 release 方法,否则会导致运行时错误。例如:

NSString *str = [[NSString alloc] initWithString:@"Hello"];
[str release];
[str release]; // 错误:重复释放
7.2 正确使用 retain release

在设置属性时,要确保正确使用 retain release 来避免内存泄漏。例如:

-(void) setName:(NSString *) value {
    [value retain];
    [name release];
    name = value;
}

如果不这样做,可能会导致原始对象的内存无法释放,从而造成内存泄漏。

7.3 及时释放不再使用的对象

在不再需要对象时,要及时释放它们,以避免不必要的内存占用。例如,在方法中创建的对象,应该在方法退出前释放。

8. 协议和代理的使用场景

协议和代理在Objective - C中有广泛的应用场景,以下是一些常见的例子:

8.1 用户交互反馈

UIAlertView 的例子中,通过实现 UIAlertViewDelegate 协议的方法,可以获取用户点击按钮的反馈信息。这在需要根据用户操作做出不同响应的场景中非常有用。

8.2 数据传递

在不同的视图控制器之间传递数据时,可以使用协议和代理。例如,一个子视图控制器可以通过代理将数据传递给父视图控制器。

8.3 事件处理

在处理各种事件时,如按钮点击、滑动手势等,可以使用协议和代理来实现事件的响应和处理。

以下是一个简单的mermaid流程图,展示协议和代理的工作流程:

graph LR
    A[发送者对象] --> B[调用代理方法]
    B --> C[代理对象]
    C --> D[实现协议方法]
    D --> E[处理事件或返回数据]
    E --> A

通过以上对Objective - C的初始化器、内存管理、协议和代理的详细介绍,我们可以更熟练地运用这些知识来开发出高效、稳定的iPhone应用程序。在实际开发中,要时刻注意内存管理的细节,合理使用协议和代理,以提高代码的质量和可维护性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值