zoukankan      html  css  js  c++  java
  • Objective-C 30分钟入门教程

    Objective-C 30分钟入门教程

    我第一次看OC觉得这个语言的语法有些怪异,为什么充满了@符号,[]符号,函数调用没有()这个,但是面向对象的高级语言也不外乎类,接口,多态,封装,继承等概念。下面会把OC里面的一些定义与Java,C++作对比,让有其他面向对象语言的同学可以快速的了解OC是个神马语言。

    1.类定义

    类用@interface定义,而不是@class,相当于Java中的class了。而Object-C中接口(Java中的接口)是用@protocol(下面有介绍)表示。

    头文件,与c++的头文件类似

     1 #import <Foundation/Foundation.h>
     2 @interface Fraction : NSObject
     3 //成员变量
     4 {
     5 @protected
     6     int numerator;
     7     int denominator;
     8 }
     9 //类方法
    10 -(void) print;
    11 //多参数函数
    12 -(id)initSetNum:(int) n over:(int) d;
    13 -(id) init;
    14 @end

    实现.m文件(相当于c中的.cpp)

     1 #import "Fraction.h"
     2 
     3 @implementation Fraction
     4 
     5 -(void) print {
     6     NSLog(@"print");
     7 }
     8 
     9 -(id)init{
    10     self = [super init];
    11     if (self != nil) {
    12         self->denominator = 1;
    13         self->numerator = 1;
    14     }
    15     return self;
    16 }
    17 -(void)set:(int)n over:(int)d {
    18     self->denominator = d;
    19     self->numerator = n;
    20 }
    21 
    22 @end

    init模板

    1 -(id)init{
    2     self = [super init];
    3     if (self) {
    4         //do init 
    5     }
    6     return self;
    7 }

    2.访问权限

    实例变量的作用域

    • @protected: 实例变量可被该类和子类中定义的方法直接访问。接口部分定义的实例变量是此作用域
    • @private: 只能被定义在此类中的方法直接访问。定义在实现部分的实例变量默认为此作用域
    • @public: 可被此类中的方法、子类或其他类直接方法(访问方法见下面点语法
    • @package: 对于64位镜像,可以在实现改类的镜像中的任何地方访问此实例变量(不了解,没用过

    @property作用域

    @property只在@interface中使用,是默认的@protected权限

    方法的作用域

    @protected,@private,@public,@package不适用实例方法,在@interface中定义的方法都是@public方法

    私有方法

    1.不在@interface中声明,直接写到@implemention里。
    2.写在空分类中

    3.@property和@synthesize

    语法

    @property(attribute1, attribute2, ...) type name;

    作用1:生成成员变量的get和set方法
    1 @interface Fraction : NSObject
    2 @property int numerator, denominator;
    3 -(void)print;
    4 @end
    1 @implementation Fraction
    2 @synthesize numerator = _abc, denominator = _def;
    3 
    4 -(void) print {
    5     self->_abc = 12;
    6     self->_def = 30;
    7 }
    8 @end
     1 #import <Foundation/Foundation.h>
     2 #import "Fraction.h"
     3 
     4 int main(int argc, const char * argv[]) {
     5     @autoreleasepool {
     6         // insert code here...
     7         Fraction * fraction = [[Fraction alloc]init];
     8         [fraction setNumerator:10];
     9         NSLog(@"numerator is %d", [fraction numerator]);
    10         
    11         fraction.numerator = 20;
    12         NSLog(@"numerator is %d", fraction.numerator);
    13     }
    14     return 0;
    15 }

    @property int numerator, denominator;

    @synthesize numerator = _abc, denominator = _def;

    两句话为我们生成了(暂时不考虑内存管理)

     1 (void) setNumerator:(int) n {
     2     //@synthesize numerator, denominator; 若是这样则
     3     //self->numerator = n;
     4     self->_abc = n;
     5 }
     6 
     7 (void) setDenominator:(int) n {
     8     self->_def = n;
     9 }
    10 
    11 (int) numerator {
    12     return self->_abc;
    13 }
    14 
    15 (int) denominator {
    16     return self->_abc;
    17 }
    如果不写@synthesize,编译器会自动生成@ synthesize name = _name

    若写@synthesize name,则相当于写了@synthesize name = name

    点语法

    oc的点语法比较特殊,c++或java定义一个实例变量,则可以this->num或instance.num就可以取得次变量了,但是oc不行!点相当于调用set和get方法,并且oc中的实例方法调用也不能用点(get set不算),比如instance.print()是不行的,要[instance print]才行

    作用2:协助内存管理

    attribute主要分三类:

    • 读写属性:(readwrite/readonly)决定是否生成set访问器,默认为readwrite
    • setter语义:(assign/retain/copy/strong/weak/unsafe_unretained)set访问器的语义,决定已何种方式对数据成员赋予新值,默认为assign
    • 原子性:(atomic/nonatomic)是否是原子性访问,默认为nonatomic

    readwrite: 生成setter/gettter方法(默认)

    readonly: 只生成getter。@synthesize不会生成setter方法,所以不能和copy/retain/assign同时使用

    assign: 简单赋值,不更改索引计数

    retain: 释放旧的对象,将旧对象的值赋予输入对象,在增加输入对象的引用计数(+1)。此属性只能用于Objective-c对象类型,不能用于基本类型和Core Foundation对象(Core Foundation?此乃何物),因为他们没有引用计数

    strong/weak/unsafe_unretained: xcode5加入的新属性,strong = retain,unsafe_unretained = assign,weak ~= assign,在引用计数为0时,对吧对象赋值为nil

    copy: 建立一个引用计数为1的对象,然后释放旧对象。此属性只对实现了NSCopying的对象类型有效

    atomic/nonatomic: setter和getter是不是原子操作,如果是atomic则在多线程情况下,setter和getter中不会被阻塞(切换线程)

    4.分类(category)、扩展(extension)和协议(protocol)

    分类

    这个概念在c++和java我找不到对应的概念,算是oc特性了

    在一个类已经定义好了的情况下,又想向类中增加一些新方法,但是有不想改原来的实现文件,或是找不到实现文件,这就是分类发挥作用的时候了,比如想在Fraction类中增加-(void)double方法,用分类可以这么搞

    MathOps.h文件

    1 #import "Fraction.h"
    2 @interface Fraction(MathOps)
    3 -(void)double;
    4 @end

    MathOps.m文件

    1 #import "Fraction.h"
    2 @implementation Fraction(MathOps)
    3 -(void)double {
    4     [self numerator] = [self numerator] * 2;
    5 }
    6 @end

    这样就可以在Fraction的实例上使用[fraction double];了

    类实例变量的扩展(extension)

    类扩展(extension)是category的一个特例,有时候也被称为匿名分类。他的作用是为一个类添加一些私有的成员变量和方法。

    类实例的扩展只能在未命名分类中定义,在命名分类中是不允许的,并且要写在响应的.m文件中

    1 #import "Fraction.h"
    2 @interface Fraction() //括号里是空就是了
    3 {
    4     int MaxNum;
    5 }
    6 
    7 @property int minNum;
    8 
    9 @end

    Objective-C第229页说,未命名分类是非常有用的,因为他们的方法都是私有的,如果要谢一个类,而且数据和方法仅供这个类本身使用,未命名分类比较合适,但是使用@selector依然可以访问,例如[A performSelector: @selector(privateTest)];

    协议@protocol

    协议有点像java中的接口,只提供方法的声明,实现由使用此协议的类来实现

    1 @protocol NSCopying
    2 -(id)copyWithZone:(NSZone *)zone;
    3 @end

    采用协议的类用<protocolName1,protocolName,...>来声明采用了protocolName协议

    @interface className: NSObject

    协议中的方法类中不用全部实现,@optional后的方法不一定要实现,@required或默认的方法一定要实现

    1 @protocol Drawing
    2 -(void)paint;   //采用的类中必须实现
    3 -(void)erase;   //采用的类中必须实现
    4 @optional
    5 -(void)outline; //采用的类中不一定实现
    6 @required
    7 -(void)showDetail; //采用的类中必须实现
    8 @end

    可以在声明对象时候指明它采用的协议,这样在赋值时由编译器检查被复制对象是否采用了协议,若没有则发出警告

    id<Drawing> currentOBject;

    协议自身也可以扩展其他的协议 @protocol Drawing3D<Drawing>

    分类也可以采用协议 @interface Fraction(Stuff)这是啥??

    5.Foundation框架

    Foundation框架,Application Kit框架,Cocoa

    框架是由许多类、方法、函数和文档按照一定的逻辑组合起来的集合,在Mac OS X系统下大约有90多个框架。

    为所有的程序开发奠定基础的框架称为Foundation框架,它允许使用一些结拜的对象,如数字和字符串,一些对象集合,如数字、字典和集合。其他功能包括处理日期和时间、自动化的内存管理、文件系统等。

    Application Kit框架包含广泛的类和方法,它们用来开发交互式的图形应用程序,使得开发文本、菜单、工具栏、表、窗口之类的过程变得十分简便(Application Kit开发Mac app,UIKit开发手持IOS ap)。术语Cocoa Touch是指Foundation、Core Data和UIKit框架。术语Cocoa是指Foundation、Core Data、Application Kit框架。

    • NSNumber: 把int、float、char等基本类型搞成对象,以便可以放到NSArray等必须存储对象的容器。
    • NSString/NSMutableString: 字符串类,一个不可变,一个可变
    • NSArray/NSMutableArray: 数组类,一个不可变,一个可变
    1 NSArray * name = [NSArray arrayWithObjects:@"zhangsan", @"lisi", @"dawang", nil];
    2 
    3 NSMutableArray * nameMutable = [NSMutableArray array];
    4 for (int i = 0; i < 10; i++) {
    5     nameMutable = [NSNumber numberWithInteger: i];
    6 }
    • NSValue: 把Foundation集合中的非对象(int、float等用NSNumber)比如C中结构体CGPoint包装(wrapping)成对象,使用时再把对象展开(unwrapping)得到CGPoint。
     1 CGPonit myPonit;
     2 NSValue * ponitObj;
     3 NSMutableArray * touchPoints = [NSMutableArray array];
     4 
     5 myPonit.x = 100;
     6 myPoint.y = 200;
     7 
     8 pointObj = [NSValue valueWithPoint:myPint];
     9 [touchPoints addObject: pointObj];
    10 
    11 myPoint = [[touchPoints lastObject]pointValue];
    • NSDictionary/NSMutableDictionary: 字典,keyValue结构
     1 NSMutableDictionary * glossary = [NSMutableDictionary dictionary];
     2 
     3 [glossary setObject:@"zhangSan" forKey:@"zhang"];
     4 [glossary setObject:@"zhaoSi" forKey:@"zhao"];
     5 
     6 NSLog(@"name zhang is: %@", [glossary objectForKey:@"zhang"]);
     7 
     8 NSDictionary * glossaryDic = 
     9 [NSDictionary dictionaryWithObjectsAndKeys:
    10 @"zhangSan", @"zhang",
    11 @"zhaoSi", @"zhao",
    12 nil];
    13 
    14 for (NSString * key in glossaryDic) {
    15     NSLog(@"%@:%@", key, [glossary objectForKey: key]);
    16 }
    • NSSet/NSMutableSet/NSIndexSet: 集合类,可用的操作包括搜索、添加、删除、比较、计算交集、计算并集等操作。

    6.ARC内存管理

    ARC(Automatic Reference Counting)自动引用计数(神马是引用计数自行脑补)。Xcode 4.2版本之后加入的新特性。Xcode4.2之前的内存管理要内存要小心的处理引用计数,retain,release,autorelease在代码中随处可见。

    XCode4.2之后的ARC是在编译的时候由编译器在代码中添加retain,release,autorelease代码,程序要要做的就是开启ARC(默认开启)功能,在@property属性参数中声明号strong,weak等属性就行了(定义变量时也可以用strong,weak等)。开启ARC后不能使用retain,release,编译不通过。

    @autorealeasepool

    任何在@autoreleasepool{}中定义的变量都是autorelease的,在出@autoreleasepool{}作用域时,会对其中的变量做一次release操作。

    Cocoa和IOS应用程序中也有这个@autoreleasepool{},也就是说Cocoa和IOS中的变量都是autorelease的,每帧循环后会对autoreleasepool中的对象做一次pop操作,把对象从autoreleasepool中删除,并做一次release。Objective-C Autorelease Pool 的实现原理这个介绍了ios中的autorealeasepool

    7.杂项

    NSLog(@"%@", object)

    NSLog添加了%@这么一个输出格式,%@对应的对象在编译后会被替换为对descriptionWithLocal方法的调用,如果此方法不存在,则替换为description方法的调用 。

    8.selector

    SEL 类成员方法的指针

    可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取.

    它的结果是一个SEL类型。这个类型本质是类方法的编号(函数地址)

    C/C++函数指针

    int test(int val)

    {
    return val+1;

    }

    int (* c_func)(int val); //定义一个函数指针变量c_func = add ; //把函数addr地址直接赋给c_func

    object-c的选择器,

    @interface foo
    -(int)add:int val;

    @end

    SEL class_func ; //定义一个类方法指针class_func = @selector(add:int);

    注意1、@selector是查找当前类(含子类)的方法。

    举例:

    父类.h文件

     1 #import <Foundation/Foundation.h>  
     2   
     3 @interface SelectorDemo : NSObject  
     4 {  
     5     SEL _methodTest;  
     6 }  
     7   
     8 @property (nonatomic,assign) SEL methodTest;//这里声明为属性方便在于外部传入。  
     9   
    10 -(void)TestParentMethod;  
    11   
    12 -(void)TestSubMethod;  
    13   
    14   
    15 @end  

    .m文件

     1 #import "SelectorDemo.h"  
     2   
     3 @implementation SelectorDemo  
     4   
     5 @synthesize methodTest = _methodTest;  
     6   
     7 -(void)parentMethod  
     8 {  
     9     NSLog(@"parent method Call Success!");  
    10 }  
    11   
    12 -(void)TestParentMethod  
    13 {  
    14     if (_methodTest)  
    15     {  
    16         [self performSelector:_methodTest withObject:nil];  
    17     }  
    18 }  
    19   
    20 -(void)TestSubMethod  
    21 {  
    22     if (_methodTest)  
    23     {  
    24         [self performSelector:_methodTest withObject:nil];  
    25     }  
    26 }  
    27   
    28 @end  

    子类.h文件

    1 #import <Foundation/Foundation.h>  
    2 #import "SelectorDemo.h"  
    3   
    4 @interface SelectorSub : SelectorDemo  
    5   
    6 @end  

    .m文件

    #import "SelectorSub.h"  
      
    @implementation SelectorSub  
      
    -(void)SubMethod  
    {  
        NSLog(@"Sub method Call Success!");  
    }  
      
    @end 

    进行测试调用。

    SelectorSub *ss = [[SelectorSub alloc]init];  
        ss.methodTest = @selector(parentMethod);  
        [ss TestParentMethod];  
        ss.methodTest = @selector(SubMethod);  
        [ss TestParentMethod];  
        [ss release]; 

    ss.methodTest = @selector(parentMethod); 这句在运行期时,会寻找到父类中的方法进行调用。

    ss.methodTest = @selector(SubMethod);//这句就在运行期时,会先寻找父类,如果父类没有,则寻找子类

    如果这里将ss.methodTest = @selector(test); 其中test即不是ss父类,也不是ss本身,也非SS子类,哪么这个时候在使用

    [self performSelector:_methodTest withObject:nil];就会出现地址寻找出错 。

    [friend performSelector:@selector(gossipAbout:) withObject:aNeighbor];

    等价于:

    [friend gossipAbout:aNeighbor]; 

    通过这个原理,当把属性设置为SEL类型时,如果回调机制使用的不是SEL声明的类或子类。想实现其它类的回调,必须传入其它类的上下文句柄。 

    #import <Foundation/Foundation.h>  
      
    @interface SelectorDemo : NSObject  
    {  
        SEL _methodTest;  
        id _handle;  
    }  
      
    @property (nonatomic,assign) SEL methodTest;  
    @property (nonatomic,retain) id handle;        //添加其它类的实例句柄属性。  
    -(void)TestParentMethod;  
    -(void)TestSubMethod;  
      
    @end 
    #import "SelectorDemo.h"  
      
    @implementation SelectorDemo  
      
    @synthesize methodTest = _methodTest;  
    @synthesize handle = _handle;  
      
    -(void)parentMethod  
    {  
        NSLog(@"parent method Call Success!");  
    }  
      
    -(void)TestParentMethod  
    {  
        if (_methodTest)  
        {  
            [_handle performSelector:_methodTest withObject:nil];//这里面原来self属为相应的实例句柄  
        }  
    }  
      
    -(void)TestSubMethod  
    {  
        if (_methodTest)  
        {  
            [_handle performSelector:_methodTest withObject:nil];  
        }  
    }  
      
    @end  

    便可如此调用

    C * c = [[SubS alloc]init];
            
    c.methodTest = @selector(Show);
    c.handle = [[AnotherC alloc]init];
            
    [c handleTest]; 

    9.Block

    有参数有返回值的demo

     1 - (void)myThirdBlock
     2 {
     3     //1.定义block
     4     double (^myThirdBlock)(double,double) = ^ (double r1,double r2){
     5         return r1 + r2;
     6     };
     7     //2.调用block
     8      double r3 = myThirdBlock(1.1,2.2);
     9     NSLog(@"有参数有返回值:%f",r3);
    10 }

    block的注意点

    1)Block内部可以访问外部变量;

    2)默认情况下,Block内部不能修改外部的局部变量

    3)给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改

    示例代码如下: 

    1 - (void)myFourBlock
    2 {
    3     int num = 5;
    4     void (^myFourBlock)() = ^{
    5     
    6         num = 6;
    7         NSLog(@"%d",num);
    8     };
    9 }

    //发现需要的再加

    结语

    本博介绍了OC的核(pi)心(mao)概念,希望看完本博可以对让你对OC有一个大体的认识,学OC是学ios的第一步, 打下坚实的OC还是很必要的。

    参考

  • 相关阅读:
    python活力练习Day13
    检测一个字符串在另外一个字符串中的位置
    Python活力练习Day12
    Python多进程与单进程效率对比
    HTML-Note
    Python判断自定义的参数格式是否正确
    图片的灰与彩
    Git常用命令
    Linux 单引号和双引号的区别
    类函数中获取进程池对象的地址
  • 原文地址:https://www.cnblogs.com/BigFeng/p/4852040.html
Copyright © 2011-2022 走看看