黑马程序员——OC学习笔记04 核心语法

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、点语法

利用点语法替代set、get方法。

点在 = 左边就是调用set方法。在 = 右边就是调用get方法。

不允许在set、get方法内使用使用点语法,会造成死循环。

<span style="font-family:SimHei;font-size:18px;">        Person *p = [[Person alloc] init];
        
        p.age = 10;//相当于[p setAge:10];
        
        int c = p.age;//相当于int c = [p age];</span>

二、类中变量的作用域

@private

只能在当前类的对象方法中直接访问(子类中有该变量,但是不能访问)如果实在想访问只能通过set方法和get方法。

@protected

可以在当前类以及子类的对象方法中直接访问(直接访问的意思是直接“_变量名”这种方式访问。)默认都是这种的。

@public

任何地方都可以访问

@package

同一个”体系内“(框架)可以访问,介于@private@public之间。



三、@property和@synthesize
@property和@synthesize会免去写set、get方法声明和实现的烦恼。

1、单独使用@property 会自动生成某个成员变量的set和get方法声明

<span style="font-family:SimHei;font-size:18px;">@interface Person : NSObject
{
    int _age;
}
@property int age;
//上面的@property会让系统自动给你生成set和get方法
- (void)setAge:(int)age;
- (int)age;

//一定要注意写法。
//注意,xcode是很笨的,如果你这样写
@property int _age;

//那么会生成下面那样的set方法和get方法。
- (void)set_age:(int)_age;
- (int)_age;
@end</span>

2、在使用了@property的前提下,在.m文件中使用@synthesize,会自动帮助你生成set、get方法实现。

<span style="font-family:SimHei;font-size:18px;">//完整的写法
//对@property int age;进行方法实现
//并且会访问_age这个成员变量。
@synthesize age = _age;
</span>

= 左边代表你要实现的哪个@property

= 右边是代表你要访问哪个成员变量。

如果你在声明文件中没有声明_age成员变量,会自动帮你生成一个_age成员变量,它的作用域是private类型的。

使用注意:

  • @synthesize age;默认会访问名字为age的成员变量,而不是_age;如果没有age就会自动生成@private类型的age成员变量。
  • @synthesize age =_age;这样就会访问_age的成员变量。如果没有_age就会自动生成@private类型的_age成员变量。


3、还有更简化的,在.m中不连@synthesize都不用写。不用声明成员变量。

<span style="font-family:SimHei;font-size:18px;">@interface Person : NSObject

@property int age;
@property int weight;

@end</span>

最简便写法有个缺点,成员变量是private的,如果想让子类直接访问,只能定义成员变量。


注意:

如果你自己写了setget方法,@property会帮你生成没写的getset方法。

如果你自己写了setget方法,那么@property就不会帮你setget方法,并且!生成_成员变量!所以要注意!


四、id类型

id是万能指针,能指向\操纵任何OC对象

定义id类型指针时不能加*,因为它本身就是指针类型。


五、构造方法

要完整的创建一个可用的对象

  • 分配存储空间
  • 初始化

之前的new方法里面就做了上面个事情。它内部调用了两个方法。

  • 分配存储空间 + alloc
  • 初始化 -init

但是new太死板,所以开发中基本不不使用。

  //不用这种方法后

 Person *p = [Personnew];

       

常用的是下面的方式:       

        //先分配内存给对象p1

       Person *p1 = [Person alloc];

        //然后把初始化后的p1p2

       Person *p2 = [p1 init];

       

       //合并到一块

       Person *p3 = [[Person alloc] init];

其中,init就是构造方法。


重写构造方法

重写构造方法的条件

  • 一定要调用回superinit方法:初始化父类声明的一些成员变量和其他属性,因为子类继承了父类所有的属性和方法。
  • [super init];返回self。因为[super init]有时候返回的对象不一定和self一样,所以为了保证一样,要对self进行赋值。self = [super init];
  • 为了保证self不为空所以要进行判断。if(self != nil)
  • 判断为真的时候,在给成员变量赋值。
	@implementation Person
	
	- (instancetype)init
	{
	    //调回super的init方法,初始化父类的属性,然后把返回值赋值给self。
	    self = [super init];
	    //判断self是否初始化成功,如果初始化成功,在进行赋值操作
	    if (self != nil) {
	        _age = 10;
	    }
	    //返回对象自己。
	    return self;
	}
	@end
根据C语言的一些特性,对以上写法进行简化

	#import "Person.h"
	
	@implementation Person
	
	
	- (instancetype)init
	{
	    if(self = [super init])
	    {
	        _age = 10;
	    }
	    return self;
	}
@end

构造方法的原则:先初始化父类,在初始化自己。

自定义构造方法

规范:

  • 一定是对象方法,一定以-开头
  • 返回值是id或者instancetype类型
  • 方法名以init开头
  • 父类的属性交个父类方法取处理,子类值处理自己的属性。
- (instancetype)initWithAge:(int)age;

- (instancetype)initWithAge:(int)age
{
    if (self = [super init]) {
        _age = age;
    }
    return self;
}



六、分类

分类Category,不改变原有类的情况下,给类添加一些方法。

使用场景:

  • 分类是依赖于类的
  • 分类名可以是人名或模块名

 

声明

@interface 类名(分类名称)

 

@end

 

实现

@implementation 类名(分类名称)

 

@end

 

分类的使用注意:

  • 分类只增加方法,不能增加成员
  • 分类方法实现中可以访问原来类声明的成员变量
  • 分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,导致我们原来的方法失效,开发中不建议这样做。
  • 调用方法优先级:分类(最后参与编译的分类优先)>原来类>父类。
  • 如果多个分类中实现了相同的方法,最后参与编译的分类有效。

 

开发过程中可以给系统自带的类添加分类。


七、类的本质

类其实也是个对象

对象都有个类型,那么类是什么类型呢,类是class类型(class里已经有*了)

类对象就是class类型创建出来得对象。

对象创建的过程

1class创建 Person类对象

 

2、利用Person类对象创建 Person类型的对象。

 

一个类在内存中只会有一个类对象。

 

//获取内存中的类对象,p是一个Person对象。

Class c =[p class];

其中c就是内存中的Person类对象。

Class c2= [Person class]

和上面的地址一样,都是获取内存中的类对象。

所以获取某个类对象有两种方法,通过对象的class方法和通过类 class方法。

 

类对象的使用:

  • 类就是类对象所以类对象可以调用类方法     [类对象 类方法];
  • 运行中类名就代表了类对象。

 

类的加载过程

  • 先加载父类,在加载子类
  • 只要加载一个类时,就会调用类的类方法+ (void)load,在类被加载的时候调用。类只会被加载一次。
  • 程序启动时,就会加载一次项目中所有的类和分类。类加载完毕后就会调用+load方法。(类和分类的load方法都会被调用)(先调用父类的load方法)分类放在所有原始类加载后加载,所以是最后调用Load方法。。
  • 当第一次使用这个类的时候,就会调用一次+(void)initialize类的初始化方法(先调用父类的initalize 初始化 方法。)
  • 分类catagory也会加载也有Load方法。
  • 如果某个类有分类,使用类的时候会优先调用分类的initalize方法覆盖类的initalize方法。
  • loadinitalize方法都只会被调用一次。(load在程序记载时使用,initalize在类被第一次使用时调用。)

八、description方法

NSObject +description类方法和-description对象方法。

 

  • 当用NSLog打印整个对象时,它会调用对象的 - description方法。
  • description返回时是NSString *类型。
  • description默认返回的是“类名和内存地址。”
  • 想要直接打印对象的属性值,就得重写description方法。
  • 不能在description中输出self,造成死循环。
  • 当尝试输出一个类对象的时候
    • 它会调用类的+description方法。
    • 拿到+description方法的返回值,显示到屏幕上。
    • 默认的返回值是类名。


九、SEL类型

SEL类型,这种类型的数据代表方法。

 

程序加载后,先给类分配存储空间,空间中存储着这个类的方法列表。每个方法都有一个SEL类型的数据与之对应。

 

每一个对象都有一个隐藏的指针指isa向它对应的类。

 

当调用p对象的 test方法时

  • test方法包装成SEL类型的数据
  • 通过isa指针去类中找SEL对应的是方法地址。
  • 根据方法地址,调用对应方法。
  • SEL其实就是方法的地址。根据SEL就能找到方法地址。

SELOC中的定义: typedefstruct objc_selector *SEL

调用方法的两种途径:

  • 直接调用方法
  • 间接调用 performSelect:
  • 单参数的 performSelector : withObject:
  • 创建SEL类型的数据:@selector(方法名),注意冒号也是方法名的一部分。

 

例如SELs = @selector(test);

 

把字符串转换成SEL数据

SEL s = NSSelectorFromString(字符串);

 

每个方法内部都有一个隐藏的SEL类型的_cmd,这个_cmd代表当前的方法。_cmd相当于@selector(test).

SEL类型是是不能直接打印的,必须转换成字符串。

NSString *str = NSStringFromSelector(_cmd);

 

总结:SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址,找到地址就能调用方法。传说中的发消息就是发SEL,消息就是SEL


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值