SlideShare ist ein Scribd-Unternehmen logo
1 von 28
深入浅出Object-C内存管理
     演讲人:冲浪
内存管理规则
        只能释放或自动释放         所拥有的对象

[MyObject alloc]
[MyObject retain];
[MyObject copy];
[MyObject autorelease];
保留计数
Objective-C使用了一   叫做持有计数(Retain Count)的机制来管理内
存中的对象。


    创建一个对象,该对象的保留计数为1。

    向一个对象发送retain消息时,该对象的保留计数加1。

    向一个对象发送release消息时,该对象的保留计数        1。

    向一个对象发送autorelease消息时,该对象的保留计数会在
    将来的某个阶段       1。
例1:
- (void)printHello {

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

    NSLog(string);

    [string release];

}

/ 用 alloc 创建对象 使 [string releaseCount] = 1
 /
例2:
- (void)printHello {

    NSString *string; = [NSString stringWithFormat:@"Hello"];

    NSLog(string);

}

/ 你拼不拥有 string ,所以你无需 release
 /



                        Q:它们差          在     里?
什么情况下用 autorelease 自动释放?
    重点:autorelease并不是“自动释放”,而是“延后释放”,在一个运行周期
    后被标记为autorelease会被释放掉。

例1:
– (NSArray *)sprockets {

   NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,
auxiliarySprocket, nil];

    return [array autorelease];

}

/ 正
 /     使用 autorelease
例2:
– (NSArray *)sprockets {

    NSArray *array = [[NSArray alloc]
initWithObjects:mainSprocket,auxiliarySprocket, nil];

   return array;

}
/ 内存泄露
 /
例3:
– (NSArray *)sprockets {

    NSArray *array = [[NSArray alloc]
initWithObjects:mainSprocket,auxiliarySprocket, nil];

   [array release];

   return array;

}
/ 程序
 /       常退出,因为 array 过早释放
例4:
– (NSArray *)sprockets {

   NSArray *array = [NSArray
arrayWithObjects:mainSprocket,auxiliarySprocket, nil];

    return array;

}

/ 你并不拥有 arrayWithObjects ,所以你不需要释放
 /
自动释放池
1.置于一个堆栈中,通常被称为被“嵌套”的。

2.新的自动释放池,它被添加到堆栈的顶部。

3. 对象收到autorelease,它被添加到当前线程栈顶的自动释放池中。

4.当被回收时,从堆栈中被删除。

5.Application Kit会在一个事件周期的   端—比如鼠标按下事件—自动创建
一个自动释放池,并且在事件周期的结尾释放它.
共享对象的有效性
例1:
heisenObject = [array objectAtIndex:n];

[array removeObjectAtIndex:n];

/ heisenObject 无效
 /
例2:
id parent = <#create a parent
object#>;

/ ...
 /

heisenObject = [parent child] ;

[parent release];

/ heisenObject 无效
 /
例3:
heisenObject = [[array objectAtIndex:n]
retain];

[array removeObjectAtIndex:n];

/ use heisenObject.
 /

[heisenObject release];

/ 正常运行
 /
集合
例1
NSMutableArray *array;

NSUInteger i;

for (i = 0; i < 10; i++) {

     NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];

     [array addObject:convenienceNumber];

}

//在这段代码中,             没有调用alloc,因此也没有必要调用release。没有必要
保留新的数字对象(convenienceNumber),因为数组会为                    代劳。
例2
NSMutableArray *array;

NSUInteger i;

for (i = 0; i < 10; i++) {

    NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger: i];

    [array addObject:allocedNumber];

    [allocedNumber release];

}

/ 由于数组在用addObject:方法添加数字时对其进行了保留 + 1,因此只要
 /
它还在数组中就不会被释放。
从方法返回的对象
例1
- (NSString *)fullName {

    NSString *string = [NSString stringWithFormat:@"%@ %@", firstName,
lastName];

     return string;

}
//    并不拥有stringWithFormat返回的字符串,所以它可以安全地从该方法
中返回
例2
- (NSString *)fullName {

  NSString *string = [[[NSString alloc] initWithFormat:@"%@
%@", firstName, lastName] autorelease];

     return string;

}
//    拥有alloc返回的字符串,但           随后向它发送了一条autorelease消
息,因此在          失去它的引用之前,         已经放弃了所有权
例3:
- (NSString *)fullName {

   NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
firstName, lastName] release];

    return string;

}

/ 错误
 /
例4:
- (NSString *)fullName {

   NSString *string = [[NSString alloc] initWithFormat:@"%@ %@",
firstName, lastName];

   return string;

}
/ 内存泄露
 /
存取方法
MyClass.h:
@interface MyClass : NSObject {
   MyObject *myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end
MyClass.m:
@synthesize myObject;

-(id)init{
    if(self = [super init]){
        MyObject * aMyObject = [[MyObject alloc] init];
        self.myObject = aMyObject;
        [aMyObject release];
    }
    return self;
}




 Q: 为什么要这么 杂的赋值? 为什么要加self. ? 直接写成self.myObject
 = [[MyObject alloc] init];不是也没有错么? 不加self有时好像也是正常的?
看以下例子retainCount变化:

间接赋值:

     1.加self.:
       MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject
retainCount = 1;
       self.myObject = aMyObject; //myObject retainCount = 2;
       [aMyObject release];//myObject retainCount = 1;

      2. 不加self.:
       MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject
retainCount = 1;
       myObject = aMyObject; //myObject retainCount = 1;
       [aMyObject release];//对象己经被释放
直接赋值:

   3.加self.:
   self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2;

   4. 不加self.:
   myObject = [[MyObject alloc] init]; //myObject retainCount = 1;
MyClass.h
@interface MyClass : NSObject {
   MyObject * _myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end


MyClass.m
@synthesize myObject = _myObject;



 如果你用 self._myObject = aMyObject; 或者 myObject = aMyObject;
 你会得到一个错误. 为什么             ?

 self.myObject = [[MyObject alloc] init]; 为什么会有内存泄露?
实现方法

get方法是:
-(MyObject*)myObject{
   return _myObject;
}



                          Set方法是:
                          / assign
                           /
                          -(void)setMyObject:(id)newValue{
                              _myObject = newValue;
                          }
/ retain
 /
-(void)setMyObject:(id)newValue{
    if (_myObject != newValue) {
        [_myObject release];
        _myObject = [newValue retain];
    }
}

                                   / copy
                                    /
                                   -(void)setMyObject:(id)newValue{
                                       if (_myObject != newValue) {
                                           [_myObject release];
                                           _myObject = [newValue copy];
                                       }
                                   }
例子

NSString* s = [[NSString alloc]initWithString:@”This is a test string”];
s = [s substringFromIndex:[s rangeOfString:@"a"].location];//内存泄露
[s release];//错误释放
谢谢

Weitere ähnliche Inhalte

Was ist angesagt?

03 Managing Memory with ARC
03 Managing Memory with ARC03 Managing Memory with ARC
03 Managing Memory with ARCTom Fan
 
Jni攻略之八――操作对象的构造方法
Jni攻略之八――操作对象的构造方法Jni攻略之八――操作对象的构造方法
Jni攻略之八――操作对象的构造方法yiditushe
 
jQuery 選取器解析
jQuery 選取器解析jQuery 選取器解析
jQuery 選取器解析Kingsley Zheng
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析Justin Lin
 
iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门Lucien Li
 
STL Container Usage Tips
STL Container Usage TipsSTL Container Usage Tips
STL Container Usage Tips永泉 韩
 
從 Singleton 談 constructor
從 Singleton 談 constructor從 Singleton 談 constructor
從 Singleton 談 constructorLuba Tang
 
OpenEJB - 另一個選擇
OpenEJB - 另一個選擇OpenEJB - 另一個選擇
OpenEJB - 另一個選擇Justin Lin
 
iOS程序设计-数据持久化
iOS程序设计-数据持久化iOS程序设计-数据持久化
iOS程序设计-数据持久化qiyutan
 
Java script closures
Java script closuresJava script closures
Java script closuresskywalker1114
 
Javascript之昨是今非
Javascript之昨是今非Javascript之昨是今非
Javascript之昨是今非Tony Deng
 
180518 ntut js and node
180518 ntut js and node180518 ntut js and node
180518 ntut js and nodePeter Yi
 
基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程zhangdaiping
 
面向对象的Js培训
面向对象的Js培训面向对象的Js培训
面向对象的Js培训yiditushe
 

Was ist angesagt? (19)

03 Managing Memory with ARC
03 Managing Memory with ARC03 Managing Memory with ARC
03 Managing Memory with ARC
 
Jni攻略之八――操作对象的构造方法
Jni攻略之八――操作对象的构造方法Jni攻略之八――操作对象的构造方法
Jni攻略之八――操作对象的构造方法
 
jQuery 選取器解析
jQuery 選取器解析jQuery 選取器解析
jQuery 選取器解析
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
 
iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门
 
STL Container Usage Tips
STL Container Usage TipsSTL Container Usage Tips
STL Container Usage Tips
 
ios分享
ios分享ios分享
ios分享
 
從 Singleton 談 constructor
從 Singleton 談 constructor從 Singleton 談 constructor
從 Singleton 談 constructor
 
OpenEJB - 另一個選擇
OpenEJB - 另一個選擇OpenEJB - 另一個選擇
OpenEJB - 另一個選擇
 
iOS程序设计-数据持久化
iOS程序设计-数据持久化iOS程序设计-数据持久化
iOS程序设计-数据持久化
 
Java script closures
Java script closuresJava script closures
Java script closures
 
Java Thread
Java ThreadJava Thread
Java Thread
 
Node way
Node wayNode way
Node way
 
JQuery Plugin
JQuery PluginJQuery Plugin
JQuery Plugin
 
Javascript之昨是今非
Javascript之昨是今非Javascript之昨是今非
Javascript之昨是今非
 
180518 ntut js and node
180518 ntut js and node180518 ntut js and node
180518 ntut js and node
 
基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程
 
面向对象的Js培训
面向对象的Js培训面向对象的Js培训
面向对象的Js培训
 
Ooredis
OoredisOoredis
Ooredis
 

Andere mochten auch

Taste Rabbitmq
Taste RabbitmqTaste Rabbitmq
Taste Rabbitmqjeff kit
 
给Geek们的音乐课
给Geek们的音乐课给Geek们的音乐课
给Geek们的音乐课jeff kit
 
构建稳健的iOS推送服务
构建稳健的iOS推送服务构建稳健的iOS推送服务
构建稳健的iOS推送服务jeff kit
 
REST is not enough: Using Push Notifications to better support your mobile cl...
REST is not enough: Using Push Notifications to better support your mobile cl...REST is not enough: Using Push Notifications to better support your mobile cl...
REST is not enough: Using Push Notifications to better support your mobile cl...Juan Gomez
 
OWD - Push Notification Server Architecture [DEVCON1_2012]
OWD - Push Notification Server Architecture [DEVCON1_2012]OWD - Push Notification Server Architecture [DEVCON1_2012]
OWD - Push Notification Server Architecture [DEVCON1_2012]Fernando Sela
 
漫游iOS开发指南
漫游iOS开发指南漫游iOS开发指南
漫游iOS开发指南jeff kit
 
Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...
Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...
Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...Congressional Budget Office
 

Andere mochten auch (7)

Taste Rabbitmq
Taste RabbitmqTaste Rabbitmq
Taste Rabbitmq
 
给Geek们的音乐课
给Geek们的音乐课给Geek们的音乐课
给Geek们的音乐课
 
构建稳健的iOS推送服务
构建稳健的iOS推送服务构建稳健的iOS推送服务
构建稳健的iOS推送服务
 
REST is not enough: Using Push Notifications to better support your mobile cl...
REST is not enough: Using Push Notifications to better support your mobile cl...REST is not enough: Using Push Notifications to better support your mobile cl...
REST is not enough: Using Push Notifications to better support your mobile cl...
 
OWD - Push Notification Server Architecture [DEVCON1_2012]
OWD - Push Notification Server Architecture [DEVCON1_2012]OWD - Push Notification Server Architecture [DEVCON1_2012]
OWD - Push Notification Server Architecture [DEVCON1_2012]
 
漫游iOS开发指南
漫游iOS开发指南漫游iOS开发指南
漫游iOS开发指南
 
Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...
Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...
Presentation on CBO’s Projections of the Costs of U.S. Nuclear Forces, 2014 t...
 

Ähnlich wie 冲浪 Object-c

iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门Lucien Li
 
Autorelease pool
Autorelease poolAutorelease pool
Autorelease poolMichael Pan
 
16 CoreData
16 CoreData16 CoreData
16 CoreDataTom Fan
 
2011中正資管學術部講座 Java-Object
2011中正資管學術部講座 Java-Object2011中正資管學術部講座 Java-Object
2011中正資管學術部講座 Java-ObjectVeck Hsiao
 
Ejb工作原理学习笔记
Ejb工作原理学习笔记Ejb工作原理学习笔记
Ejb工作原理学习笔记yiditushe
 
Java script closures
Java script closuresJava script closures
Java script closuresskywalker1114
 
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用FLASH开发者交流会
 
Kissy简介
Kissy简介Kissy简介
Kissy简介jay li
 
Keep your code clean
Keep your code cleanKeep your code clean
Keep your code cleanmacrochen
 

Ähnlich wie 冲浪 Object-c (11)

iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门
 
Autorelease pool
Autorelease poolAutorelease pool
Autorelease pool
 
16 CoreData
16 CoreData16 CoreData
16 CoreData
 
2011中正資管學術部講座 Java-Object
2011中正資管學術部講座 Java-Object2011中正資管學術部講座 Java-Object
2011中正資管學術部講座 Java-Object
 
Ejb工作原理学习笔记
Ejb工作原理学习笔记Ejb工作原理学习笔记
Ejb工作原理学习笔记
 
Java script closures
Java script closuresJava script closures
Java script closures
 
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
 
Kissy简介
Kissy简介Kissy简介
Kissy简介
 
Keep your code clean
Keep your code cleanKeep your code clean
Keep your code clean
 
Java物件導向
Java物件導向Java物件導向
Java物件導向
 
Sun java
Sun javaSun java
Sun java
 

Mehr von jeff kit

Scala jeff
Scala jeffScala jeff
Scala jeffjeff kit
 
原创音乐会
原创音乐会原创音乐会
原创音乐会jeff kit
 
杯具是怎样练成的
杯具是怎样练成的杯具是怎样练成的
杯具是怎样练成的jeff kit
 
Brief intro of Dropbox
Brief intro of DropboxBrief intro of Dropbox
Brief intro of Dropboxjeff kit
 

Mehr von jeff kit (7)

Scala jeff
Scala jeffScala jeff
Scala jeff
 
原创音乐会
原创音乐会原创音乐会
原创音乐会
 
杯具是怎样练成的
杯具是怎样练成的杯具是怎样练成的
杯具是怎样练成的
 
Bi
BiBi
Bi
 
Git
GitGit
Git
 
Autoforms
AutoformsAutoforms
Autoforms
 
Brief intro of Dropbox
Brief intro of DropboxBrief intro of Dropbox
Brief intro of Dropbox
 

冲浪 Object-c

  • 2. 内存管理规则 只能释放或自动释放 所拥有的对象 [MyObject alloc] [MyObject retain]; [MyObject copy]; [MyObject autorelease];
  • 3. 保留计数 Objective-C使用了一 叫做持有计数(Retain Count)的机制来管理内 存中的对象。 创建一个对象,该对象的保留计数为1。 向一个对象发送retain消息时,该对象的保留计数加1。 向一个对象发送release消息时,该对象的保留计数 1。 向一个对象发送autorelease消息时,该对象的保留计数会在 将来的某个阶段 1。
  • 4. 例1: - (void)printHello { NSString *string = [[NSString alloc] initWithString:@"Hello"]; NSLog(string); [string release]; } / 用 alloc 创建对象 使 [string releaseCount] = 1 /
  • 5. 例2: - (void)printHello { NSString *string; = [NSString stringWithFormat:@"Hello"]; NSLog(string); } / 你拼不拥有 string ,所以你无需 release / Q:它们差 在 里?
  • 6. 什么情况下用 autorelease 自动释放? 重点:autorelease并不是“自动释放”,而是“延后释放”,在一个运行周期 后被标记为autorelease会被释放掉。 例1: – (NSArray *)sprockets { NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket, auxiliarySprocket, nil]; return [array autorelease]; } / 正 / 使用 autorelease
  • 7. 例2: – (NSArray *)sprockets { NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,auxiliarySprocket, nil]; return array; } / 内存泄露 /
  • 8. 例3: – (NSArray *)sprockets { NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,auxiliarySprocket, nil]; [array release]; return array; } / 程序 / 常退出,因为 array 过早释放
  • 9. 例4: – (NSArray *)sprockets { NSArray *array = [NSArray arrayWithObjects:mainSprocket,auxiliarySprocket, nil]; return array; } / 你并不拥有 arrayWithObjects ,所以你不需要释放 /
  • 11. 共享对象的有效性 例1: heisenObject = [array objectAtIndex:n]; [array removeObjectAtIndex:n]; / heisenObject 无效 /
  • 12. 例2: id parent = <#create a parent object#>; / ... / heisenObject = [parent child] ; [parent release]; / heisenObject 无效 /
  • 13. 例3: heisenObject = [[array objectAtIndex:n] retain]; [array removeObjectAtIndex:n]; / use heisenObject. / [heisenObject release]; / 正常运行 /
  • 14. 集合 例1 NSMutableArray *array; NSUInteger i; for (i = 0; i < 10; i++) { NSNumber *convenienceNumber = [NSNumber numberWithInteger:i]; [array addObject:convenienceNumber]; } //在这段代码中, 没有调用alloc,因此也没有必要调用release。没有必要 保留新的数字对象(convenienceNumber),因为数组会为 代劳。
  • 15. 例2 NSMutableArray *array; NSUInteger i; for (i = 0; i < 10; i++) { NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger: i]; [array addObject:allocedNumber]; [allocedNumber release]; } / 由于数组在用addObject:方法添加数字时对其进行了保留 + 1,因此只要 / 它还在数组中就不会被释放。
  • 16. 从方法返回的对象 例1 - (NSString *)fullName { NSString *string = [NSString stringWithFormat:@"%@ %@", firstName, lastName]; return string; } // 并不拥有stringWithFormat返回的字符串,所以它可以安全地从该方法 中返回
  • 17. 例2 - (NSString *)fullName { NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName] autorelease]; return string; } // 拥有alloc返回的字符串,但 随后向它发送了一条autorelease消 息,因此在 失去它的引用之前, 已经放弃了所有权
  • 18. 例3: - (NSString *)fullName { NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName] release]; return string; } / 错误 /
  • 19. 例4: - (NSString *)fullName { NSString *string = [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; return string; } / 内存泄露 /
  • 20. 存取方法 MyClass.h: @interface MyClass : NSObject { MyObject *myObject; } @property (nonatomic, retain) MyObject *myObject; @end
  • 21. MyClass.m: @synthesize myObject; -(id)init{ if(self = [super init]){ MyObject * aMyObject = [[MyObject alloc] init]; self.myObject = aMyObject; [aMyObject release]; } return self; } Q: 为什么要这么 杂的赋值? 为什么要加self. ? 直接写成self.myObject = [[MyObject alloc] init];不是也没有错么? 不加self有时好像也是正常的?
  • 22. 看以下例子retainCount变化: 间接赋值: 1.加self.: MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1; self.myObject = aMyObject; //myObject retainCount = 2; [aMyObject release];//myObject retainCount = 1; 2. 不加self.: MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1; myObject = aMyObject; //myObject retainCount = 1; [aMyObject release];//对象己经被释放
  • 23. 直接赋值: 3.加self.: self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2; 4. 不加self.: myObject = [[MyObject alloc] init]; //myObject retainCount = 1;
  • 24. MyClass.h @interface MyClass : NSObject { MyObject * _myObject; } @property (nonatomic, retain) MyObject *myObject; @end MyClass.m @synthesize myObject = _myObject; 如果你用 self._myObject = aMyObject; 或者 myObject = aMyObject; 你会得到一个错误. 为什么 ? self.myObject = [[MyObject alloc] init]; 为什么会有内存泄露?
  • 25. 实现方法 get方法是: -(MyObject*)myObject{ return _myObject; } Set方法是: / assign / -(void)setMyObject:(id)newValue{ _myObject = newValue; }
  • 26. / retain / -(void)setMyObject:(id)newValue{ if (_myObject != newValue) { [_myObject release]; _myObject = [newValue retain]; } } / copy / -(void)setMyObject:(id)newValue{ if (_myObject != newValue) { [_myObject release]; _myObject = [newValue copy]; } }
  • 27. 例子 NSString* s = [[NSString alloc]initWithString:@”This is a test string”]; s = [s substringFromIndex:[s rangeOfString:@"a"].location];//内存泄露 [s release];//错误释放

Hinweis der Redaktion

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n