面向对象2

方法详解

方法的所属性

  • 方法不能独立定义,hi能在类体力定义。
  • 从逻辑意义上看,方法要么属于类本很,要么属于类的一个对象
  • 不能独立执行方法,执行方法必须使用类或对象作为调用者

形参个数可变的方法

定义形参个数可变的方法时,在最后一个形参名后增加逗号和三个英文句点 , …
- va_list:这是一个类型,用于定义指向可变参数列表的指针变量。
- va_start:这是一个函数,该函数指定开始处理可变形参的列表,并让指针变量指向可变形参列表的第一个参数。
- va_end:结束处理可变形参,释放指针变量。
- var_arg:该函数返回获取指针当前指向的参数的值,并将指针移动到下一个参数

@imlementation VarAygs
- (void)test:(NSString *) name, ...
{
    //使用va_list定义一个argList指针变量,该指针变量会指向可变参数列表
    va_list argList;
    //如果 name 参数存在,则处理后面可变参数
    if (name)
    {
        //由于 name 参数并不在可变参数列表中,因此先处理 name 参数
        NSLog(@"%@", name);
        //让argList指向可变参数列表的第一个参数
        va_start(argList, name);
        //va_arg用于提取argList指针当前指向的参数,并将指针移动到下一个参数
        //arg变量用于保存获取的参数,如果参数部位nil,则进入循环体
        NSString* arg = va_arg(argList, id);
        while(arg)
        {
            //输出参数
            NSLog(@"%@",arg);
            //再一次提取参数赋予arg,病将指针移动到下一个参数
            arg = va_arg(argList, id);
        }
        //释放argList指针,结束提取
        va_end(argList);
    }
}
@end
int main(int argc, char * argv[])
{
    @autoreleasepool
    {
        VarArgs* va = [[VarArgs alloc] init];
        [va test:@"MyTest01", @"MyTest02", @"MyTest03", nil];
    }
}

成员变量

@interface MyPerson:NSObject
{
    @public
    //定义2个实例/成员变量
    NSString* _name;
    int _age;
}
@end
@implementation MyPerson
@end
int main(int ago, char * argv[])
{
    @autoreleasepool
    {
        //创建MyPerson对象
        MyPerson p = [[MyPerson alloc] init];
        //通过指针变量访问 p 的成员变量
        NSLog(@"the name of p is: %@, the age of p is: %D",p->_name, p->_age);
        //直接为 p 的name赋值
        p->_name = @"p_Person";
        p->_age = 30;
        //再次通过指针变量访问 p 的成员变量
        NSLog(@"then, the name of p is: %@, the age of p is: %d",p->_name, p->_age)
        //创建两个MyPerson的对象
        MyPerson p1 = [[MyPerson alloc] init];
        MyPerson p2 = [[MyPerson alloc] init];
        //为两个对象的 _name 属性赋值
        p1->_name = @"张三";
        p2->_name = @"孙悟空";
    }
}

为p1 p2的属性赋值的存储示意图如下:
为p1 p2的属性赋值的存储示意图

模拟类变量

可通过在实现部分来添加static全局变量来模拟类变量
接口部分:

@interface MyUser:NSobject
+ (NSString*) nation;
+ (void) setNation: (NSString*) newNation;
@end

实现部分:

static NSString* nation = nil;
@implementation MyUser
+ (NSString*) nation
{
    return nation;
}
+ (void) setNation: (NSString*) newNation
{
    if(![nation isEqualToString: newNation])
    {
        nation = newNation;
    }
}
@end

main()部分:

#import "MyUser.h"
int main(int argh, char * argv[])
{
    @autoreleasepool
    {
        //为MyUser的类变量赋值
        [MyUser setNation:@"China"];
        //访问MyUser的类变量的值
        NSLog(@"the class variable value is: %@", [MyUser nation]);
    }
}

单例模式(singleton)

如果一个类始终只能创建一个实例,则这个类被称为单例类。
在实现部分用static全局变量获取实例的方式来实现。
接口部分:

#import <Foundation/Fundation.h>
@interface MySingleton: NSobject
+ (id) intense;
@end

实现部分和main():

#import "MySingleton.h"
static id instance = nil;
@implementation MySingleton
+ (id) intense
{
    //如果instance为nil
    if(!instance)
    {
        //创建一个Singleton实例,并将该实例赋给instance全局变量
        instance = [[super alloc] init];
    }
    return instance;
}
@end
int main(int argh, char * argv[])
{
    @autoreleasepool
    {
        //判断两次获取的实例是否相当
        NSLog(@"相等则返回1,不等则返回0:%d",[MySingleton instance] == [MySingleton instance]);
    }
}

隐藏和封装

理解封装

封装(Encapsulation)是面向对象的三大特征之一(另外两个是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,二是通过该类所提供的方法来实现对内部信息的操作和访问。
封装的目的:

  • 隐藏类的实现细节
  • 使用者只能通过预定的方法访问数据,从而可以在方法里面假如控制逻辑,限制对成员变量的不合理访问
  • 可进行数据查询,从而有利于保证对象信息的完整性
  • 便于修改,提高代码的可维护性

实现良好的封装,需要从以下两个方面考虑:

  • 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。
  • 把方法暴露出来,让方法控制对这些成员变量进行安全的访问和操作。

使用访问控制符

OC的访问控制符为 @private、@package、@protected、@public,作用范围是从他们的出现位置开始,到下一个访问控制符出现或右花括号 } 。
4个访问控制符及其权限如下:

@private@package@protected@public
同一个类中****
同一个映像中**
子类中**
全局范围内*

访问控制符的使用基本原则:

  • 类里绝大部分成员变量都应该使用 @private 限制。此外有些方法只是用于辅助实现该类的其他方法,这些方法被称为工具方法,这些工具方法也应该隐藏在该类的内部,此时就应该吧中心和谐类方法定义在类的实现部分。
  • 如果某个类主要作为其他类的父类,该类力包含的成员变量希望被子类访问,则可以考虑用 @protected 限制这些成员变量
  • 希望暴露出来给其他类自由调用的方法应该现在类接口部分定义,然后在类实现部分实现他们。

合成存取方法

合成存取方法有如下两步:

  • 在类接口部分用 @propert 定义属性。使用 property定义属性是无需放在花括号中,直接放在 @interface 和 @end 之间即可
  • 在类实现的部分使用 @synthesize 指令声明该属性即可,格式如下
@synthesize propertyName [= 成员变量名];

接口部分:

#import <Foundation/Foundation.h>
@interface MyUser: NSObject
//用 @property 定义property
@properrty (nonatomic) NSString * name;
@property NSString * pass;
@end

实现部分:

@import "NyUser.h"
@implementation MyUser
//为2个property合成setter和getter方法
@synthesize name = _name;    //指定name property底层对应的成员变量名为 _name
@synthesize pass;    //会自动生成一个含有下划线的同名成员变量,但其实就是 _pass
//实现自定义的 setName: 方法,此方法会覆盖掉合成的setter方法
- (void) setName:(NSString*) name
{
    self->_name = [NSString stringWithFormat:@"+++%@", name];
}
@end

main()部分:

#import "MyUser.h"
int main(int argh, char * atgv[])
{
    @autoreleasepool
    {
        MyUser* user = [[MyUser alloc] init];
        [user setName:@"admin"];
        [user serPass:@"1234"];
        NSLog(@"User's name is: %@, user's pass is: %d",[user name],[user pass]);
    }
}

@propert 定义property时,可以在 @property 和类型之间用括号添加一些额外的指示符:

@property (nonatomic) NSString * name;

指示符主要有:

  • assign:指定对属性只是进行简单赋值,不更改对所赋的值的引用计数。
    ps:引用计数是OC内存回收的概念,当一个对象的引用计数大于0时,表明该对象还不应该被回收,由于SNInteger等基础类型,以及short、float、double、结构体等C数据类型不存在回收问题,故此适用。
  • atomic(nonatomic):指定合成的存取方法是否为原子操作。所谓院子操作,主要指是否线程安全。如果使用atomic,那么合成的存取方法都是线程安全的——当一个线程进入存取方法的方法体之后,其他线程无法进入该存取方法,这样就可以避免多线程并发破坏对象的数据完整性,atomic是默认值。虽然atomic可以保证对象数据的完整性,但atomic得线程安全会造成性能下降,因此,大多数单线程环境下,我们都会考虑使用nonatomic来提高存取方法的访问性能
  • copy:如果使用copy指示符,当调用setter方法对成员变量赋值时,会将被赋值的对象复制一个副本,再讲该副本赋值给成员变量。copy会将院成员变量所引用对象的引用计数减1.当成员变量的类型是可变类型或其子类是可变类型时,被赋值的对象有可能在赋值之后被修改,如果程序不需要这种修改映像setter方法设置的成员变量的值,此时就可考虑使用copy指示符。
  • getter、setter:用于为合成的存取方法制定自定义方法名。
@import <Foundation/Foundation.h>
@interface MyItemLNSObject
@property (assign, nonatomic, getter=wawa, setter=nana:) int price;
@end
@import "MyItem.h"
int main()
{
    @autoreleasepool
    {
        MyItem* item = [[MyItem alloc] init];
        [item nana:30];    //设置price属性
        NSLog(@"item's price is: $d", [item wawa]);
    }
}
  • readonly、readwrite:readonly指示系统值合成getter方法,不合成setter方法;readwirite指示系统合成getter和setter方法,为默认值
  • retain:当把某个对象赋值给该属性时,该属性原来所引用的对象的引用计数减1,被赋值对象的引用计数加1
  • strong、weak:strong指定该属性对被赋值对象持有强引用性;weak制定该属性对被赋值对象持有弱引用性。强引用的意思是:只要该强引用指向被赋值的对象,那么该对象就不会自动回收。弱引用的意思是:即使该弱引用指向被赋值对象,该对象也可能被回收。
  • unsafe_unretained:与weak基本相似,不同的是用 unsafe_unretained指针所引用对象被回收之后,指针不会被赋为nil,因此可能导致程序崩溃,故而一般都选择使用weak,而不用unsafe_unreained
    PS:在启动ARC机制时,使用strong、weak指示符将十分方便。如果程序不希望被该属性引用的对象被回收,则用strong指示符;如果程序需要保证性能,避免内存溢出,则用weak指示符。对于声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil,这样能有效避免悬空指针(指针指向对象已删除的指针)。

使用点语法访问属性

MyItem* item = [[MyItem alloc] init];
item.nana = 30;    //设置price属性的语句,等价于[item nana:30];
int i = item.nana;    //将price属性的值赋予 i ,等价于 int i = [item wawa]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值