语法基础

Introduction

Coding Style

参考资料

数据结构

Basic

id

id 在 Objective C 中是一个类型,一个 complier 所认可的 Objective C 类型,跟 void *是不一样的,比如一个 id userName, 和 void *pUserName,[userName print] 是可以的,但[pUserName print] 在编译时就会报错,因为 ObjeciveC 的编译器看到 id,会假定它可以接受任何 message,虽然在 runtime 时可能并不是这样的,但 pUserName 并不是 Objective C 类型,编译器就会报错,但是 void *有可能时可以接收 print message 的。

/**
 * Type for Objective-C objects.
 */
typedef struct objc_object
{
    /**
     * Pointer to this object's class.  Accessing this directly is STRONGLY
     * discouraged.  You are recommended to use object_getClass() instead.
     */
#ifndef __OBJC_RUNTIME_INTERNAL__
    __attribute__((deprecated))
#endif
    Class isa;
} *id;

SEL:函数指针

其作用相当于函数指针,现在我看到的大多说用法都是在调用某些函数需要传递一个函数指针参数时,使用@selector。它会在当前类里面查找 selector 后面所跟的函数,返回一个 SEL 类型的值。

譬如:

[对象 performSelector:SEL变量 withObject:参数1 withObject:参数2];

既代指使用 performSelecor 方法来执行。在调用 respondsToSelector:@selector(method)时,这个 method 只有在该方法存在参数时需要 “:",如果该方法不需要参数就不需要加这个冒号。否则,编译不会报错,只是执行返回的值不对。当然如果方法有多个参数,需要多个冒号,参数有名称的需要带上参数名称。换言之,就是需要带上完整的参数名。

如:有如下方法:

-(NSString*)toXmlString;

此时调用类似于:

[self respondsToSelector:@selector(toXmlString)]

如果 toXmlString 方法的定义为:

-(NSString*)toXmlString:(NSString*)prefix;

那么调用就必须加上冒号,如:[self respondsToSelector:@selector(toXmlString:)]

NSNumber(数值类型)

NSNumber 是 OJC 中的数字对象,可以使用 NSNumber 对象来创建和初始化不同类型的数字对象。

    #import <Foundation/Foundation.h>

    NSNumber *myNumber,*floatNumber,*intNumber,*number1;

    //创建integer类型对象
    intNumber = [NSNumber numberWithInteger:123];
    NSLog(@"%i",[intNumber integerValue]);

    //创建long类型对象
    myNumber = [NSNumber numberWithLong:0xababab];
    NSLog(@"%lx",[myNumber longValue]);

    //创建char类型对象
    myNumber = [NSNumber numberWithChar:'K'];
    NSLog(@"%c",[myNumber charValue]);

    //创建float类型对象
    floatNumber = [NSNumber numberWithFloat:123.00];
    NSLog(@"%f",[floatNumber floatValue]);

    //创建double类型对象
    myNumber = [NSNumber numberWithDouble:112233e+15];
    NSLog(@"%lg",[myNumber doubleValue]);

    //从字符串中创建NSNumber对象,一般是先用NSString自带的*Value类型方法转化为Primitive类型,再用NSNumber的number*类型方法进行转化。
result=[NSNumber numberWithFloat:[ss floatValue]];

    //判断两个对象的值是否相等
    if ([intNumber isEqualToNumber:floatNumber] == YES ) {
       NSLog(@"值相等");
    } else {
       NSLog(@"值不相等");
    }

    //比较两个对象的值大小
    if ( [intNumber compare:myNumber] == NSOrderedAscending) {
       NSLog(@"左边的数字小");
    } else {
       NSLog(@"左边的数字大");
    }

    //初始化实例
    number1 = [[NSNumber alloc] initWithInt:1000];
    NSLog(@"%d",[number1 intValueunsigned];
    [number1 release];

DateTime

NSDate

如果需要获取到今日日期:

NSDate *now = [[NSDate alloc] init];

不过需要注意的是,OC 获取到的默认是格林尼治时间,所以需要获取到本地时间的话别忘了转换时区。Cocoa 的 Date 也非常方便的提供了计算功能,可以基于时间间隔来获取新的时间,譬如可以获取明天或者昨天:

NSTimeInterval secondsPerDay = 24 * 60 * 60;
NSDate *tomorrow = [[NSDate alloc]
            initWithTimeIntervalSinceNow:secondsPerDay];
NSDate *yesterday = [[NSDate alloc]
            initWithTimeIntervalSinceNow:-secondsPerDay];
[tomorrow release];
[yesterday release];

也可以在今天的基础上进行加减:

NSTimeInterval secondsPerDay = 24 * 60 * 60;
NSDate *today = [[NSDate alloc] init];
NSDate *tomorrow, *yesterday;
tomorrow = [today dateByAddingTimeInterval: secondsPerDay];
yesterday = [today dateByAddingTimeInterval: -secondsPerDay];
[today release];

本地化时间

@implementation NSDate(Utils)
-(NSDate *) toLocalTime
{
  NSTimeZone *tz = [NSTimeZone localTimeZone];
  NSInteger seconds = [tz secondsFromGMTForDate: self];
  return [NSDate dateWithTimeInterval: seconds sinceDate: self];
}
-(NSDate *) toGlobalTime
{
  NSTimeZone *tz = [NSTimeZone defaultTimeZone];
  NSInteger seconds = -[tz secondsFromGMTForDate: self];
  return [NSDate dateWithTimeInterval: seconds sinceDate: self];
}
@end

注意,这种情况下的转化而来的标准时间,尚为

时间差

NSDate 自带有timeIntervalSinceDate方法,可以计算两个 NSDate 之间的时间间隔(秒级别)。

NSDate *startDate = ...;
NSDate *endDate = ...;

NSCalendar *gregorian = [[NSCalendar alloc]
                 initWithCalendarIdentifier:NSGregorianCalendar];

NSUInteger unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;

NSDateComponents *components = [gregorian components:unitFlags
                                          fromDate:startDate
                                          toDate:endDate options:0];
NSInteger months = [components month];
NSInteger days = [components day];

时间比较

  • 判断是否为同一天
double timezoneFix = [NSTimeZone localTimeZone].secondsFromGMT;
if (
    (int)(([nowDate timeIntervalSince1970] + timezoneFix)/(24*3600)) -
    (int)(([lastDate timeIntervalSince1970] + timezoneFix)/(24*3600))
    == 0)
{
    return YES;
}

NSDateFormatter

在开发 iOS 程序时,有时候需要将时间格式调整成自己希望的格式,这个时候我们可以用 NSDateFormatter 类来处理。

例如:

//实例化一个NSDateFormatter对象
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

//设定时间格式,这里可以设置成自己需要的格式
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

//用[NSDate date]可以获取系统当前时间
NSString *currentDateStr = [dateFormatter stringFromDate:[NSDate date]];

//输出格式为:2010-10-27 10:22:13
NSLog(@%@,currentDateStr);

//alloc后对不使用的对象别忘了release
[dateFormatter release];

输出格式通 setDateStyle 和 setTimeStyle 设置,分别定义的日期和时间的格式可选一下的系统给出的方法:

typedef enum {
    NSDateFormatterNoStyle     = kCFDateFormatterNoStyle,
    NSDateFormatterShortStyle  = kCFDateFormatterShortStyle,//“11/23/37” or “3:30pm”
    NSDateFormatterMediumStyle = kCFDateFormatterMediumStyle,//\"Nov 23, 1937\"
    NSDateFormatterLongStyle   = kCFDateFormatterLongStyle,//\"November 23, 1937” or “3:30:32pm\"
    NSDateFormatterFullStyle   = kCFDateFormatterFullStyle//“Tuesday, April 12, 1952 AD” or “3:30:42pm PST”
} NSDateFormatterStyle;

除了这种传统的时间格式化需求之外,往往我们还需要将时间转化为譬如几分钟前,几小时前这种方式,可以参考如下代码:

+(NSString *)dateFormaterString:(NSString *)stringDate
{
    //为空判断
    if([stringDate isEqualToString:@""] || [stringDate isEqualToString:@"-10086"] || !stringDate){
        return @"未知时间";
    }

    NSDate *time = [NSDate dateWithTimeIntervalSince1970:([stringDate integerValue]/1000)];
    YLMoment *moment = [[YLMoment alloc] initWithDate:time];
    NSString *fromNow = [moment fromNow];

    if ([fromNow rangeOfString:@"years"].length>0||[fromNow rangeOfString:@"months"].length>0||[fromNow rangeOfString:@"days"].length>0||[fromNow rangeOfString:@"day"].length>0)
    {
        //不属于今天
        fromNow = [moment format:@"M月d日 HH:mm"];

    }
    else//对于一天当中,具体情况的处理 an hour ago
    {
        NSArray *temArray = [fromNow componentsSeparatedByString:@" "];

        if([fromNow rangeOfString:@"hours"].length > 0||[fromNow rangeOfString:@"hour"].length > 0)
        {

            if ([temArray[0] isEqualToString:@"an"]) {
                fromNow = @"1小时前";
            }else{
                fromNow = [NSString stringWithFormat:@"%@小时前",temArray[0]];
            }
        }
        else//秒统一处理成一分钟前、分钟 a few seconds ago
        {
            if ([temArray[0] isEqualToString:@"a"]) {
                fromNow = @"1分钟前";
            }else{
                fromNow = [NSString stringWithFormat:@"%@分钟前",temArray[0]];
            }
        }

    }

    return fromNow;
}

YLMoment

String

参考资料

    //求字符串对象的长度
    NSInteger length = [str8 length]
    NSLog( @"%ld", length )

    //判断一个字符串对象是否拥有前缀字符串
    BOOL result1 = [str8 hasPrefix:@"If"]
    NSLog( @"%@", result1 ? @"YES" : @"NO" )

    //判断一个字符串对象是否拥有后缀
    BOOL result2 = [str8 hasSuffix:@"en"]
    NSLog( @"%@", result2 ? @"YES" : @"NO" )

    //判断一个字符串对象是否和另一个字符串对象相同
    BOOL result3 = [str8 isEqualToString:str7]
    NSLog( @"%@", result3 ? @"YES" : @"NO" )
    NSLog( @"%d", result3 )

创建增删

    //使用初始化方法创建
    //    NSString *str1 = [[NSString alloc] initWithString:@"name"]
    NSString *str1 = @"name"
    NSLog( @"%@", str1 )
    //    NSString *str2 = [NSString stringWithString:@"name"]
    NSString *str2 = @"name"
    NSLog( @"%@", str2 )

    char *cStr = "haha"
    //将C语言字符串转成OC的对象
    NSString *str3 = [[NSString alloc] initWithCString:cStr encoding:NSUTF8StringEncoding]
    NSLog( @"%@", str3 )

    NSString *str4 = [NSString stringWithCString:cStr encoding:NSUTF8StringEncoding]
    NSLog( @"%@", str4 )

    //根据指定格式创建字符串
    NSString *str5 = [[NSString alloc] initWithFormat:@"%@+%d", @"en", 1001]
    NSLog( @"%@", str5 )

    NSString *str6 = [NSString stringWithFormat:@"%@+%d", @"ne", 10014]
    NSLog( @"%@", str6 )

    //根据指定路径的文件内容创建字符串对象
    NSString *str7 = [[NSString alloc] initWithContentsOfFile:@"/Users/lanouhn/Desktop/test.txt" encoding:NSUTF8StringEncoding error:nil]
    NSLog( @"%@", str7 )

    //    NSError *err = [NSError init]
    NSString *str8 = [NSString stringWithContentsOfFile:@"/Users/lanouhn/Desktop/words.txt" encoding:NSUTF8StringEncoding error:nil]
    NSLog( @"%@", str8 )

索引遍历

    //字符串比较排序结果
    NSComparisonResult result4 = [str8 compare:str7]
    NSLog( @"%ld", result4 )

    //获取子字符串
    //从指定下标的字符开始(包含改字符)到字符串结束的子字符串,下标从 0 开始。
    NSString *subStr1 = [str8 substringFromIndex:1]
    NSLog( @"%@", subStr1 )

    //从下标为0的字符开始到指定的下标结束,此范围内的字符串
    NSString *subStr2 = [str8 substringToIndex:2]
    NSLog( @"%@", subStr2 )

    //NSRange 为结构体类型,成员location描述下标位置,成员length描述需要截取的字符串长度
    NSRange rang = NSMakeRange(1, 3)
    //    rang.length = 4
    //    rang.location = 2
    NSString *subStr3 = [str8 substringWithRange:rang]
    NSLog( @"%@", subStr3 )

    //字符串拼接
    //根据给定的参数字符串拼接并产生新的字符串,不改变原有的字符串。(不可变字符串)
    NSString *newString1 = [str8 stringByAppendingString:@"+100"]
    NSLog( @"%@", newString1 )

    //根据给定的格式串以及参数拼接产生新的字符串
    NSString *newString2 = [str8 stringByAppendingFormat:@"%d", 1001]
    NSLog( @"%@", newString2 )

    //路径拼接
    NSString *newString3 = [str8 stringByAppendingPathComponent:@"xx.avi"]
    NSLog( @"%@", newString3 )

    //字符串替换
    //通过给定的第二字符串替换str8中存在的字符串,所有的都替换
    NSString *newString4 = [str8 stringByReplacingOccurrencesOfString:@"e" withString:@"呵呵"]
    NSLog( @"%@", newString4 )

    //字符串与数值类型数据的转换
    NSString *numString1 = @"1"
    NSInteger integerValue = [numString1 integerValue]
    NSLog( @"%ld", integerValue )
    float integerValue1 = [numString1 floatValue]
    NSLog( @"%f", integerValue1 )

    //大小写转换
    NSString *string = @" i love you"
    //转成大写字符
    NSString *upperCaseStr = [string uppercaseString]
    NSLog( @"%@", upperCaseStr )
    //转成小写字符
    NSString *lowercaseStr = [upperCaseStr lowercaseString]
    NSLog( @"%@", lowercaseStr )

    //转成首字母大写字符串(每个字符串的首字母转成大写的)
    NSString *capitalString = [string capitalizedString]
    NSLog( @"%@", capitalString )

其他操作

Format

NSMutableString

    NSMutableString *mutableStr1 = [[NSMutableString alloc] init]
    NSLog( @"%@", mutableStr1 )
    NSMutableString *mutableStr2 = [NSMutableString string]

    //可变字符串的拼接
    [mutableStr1 appendString:@"abcdef"]
    NSLog( @"%@", mutableStr1 )
    NSString *resultString = [mutableStr1 stringByAppendingString:@"xxxx"]

    NSLog( @"%@", resultString )
    NSLog( @"%@", mutableStr1 )

    //另一个字符串拼接方法
    //stringByAppendingFormat : appendFormat
    [mutableStr2 appendFormat:@"wang+%d", 1001]
    NSLog( @"%@", mutableStr2 )

    //删除字符串
    [mutableStr2 deleteCharactersInRange:NSMakeRange(2, 4)]
    NSLog( @"%@", mutableStr2 )

    //插入字符串
    //在给定的下标之前插入指定的字符串(前插)
    [mutableStr2 insertString:@"123" atIndex:2]
    NSLog( @"%@", mutableStr2 )

    //替换字符串
    //根据给定的字符串的替换指定范围的字符们
    [mutableStr2 replaceCharactersInRange:NSMakeRange(0, 3) withString:@"xxx"]
    NSLog( @"%@", mutableStr2 )

Array

Array

NSArray:不可变数组

[array count] : 数组的长度。

[array objectAtIndex 0]: 传入数组脚标的 id 得到数据对象。

[arrayWithObjects; …] :向数组对象初始化赋值。这里可以写任意对象的指针,结尾必须使用 nil。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        NSObject *obj = [[NSObject alloc]init];
        NSArray *array = [NSArray arrayWithObjects:
                                   @"a",
                                    obj,
                                   @"c",nil];
        NSLog(@"array Count:%lu",[array count]);
        //遍历数组
        for (NSObject *object in array) {
            NSLog(@"数组对象:%@", object);
        }
        [obj release];
    }


    return 0;
}

NSMutableArray 可变对象数组

  • 创建增删

[NSMutableArray arrayWithCapacity:6] :初始化可变数组对象的长度,如果后面代码继续添加数组超过长度 6 以后 NSMutableArray 的长度会自动扩充,6 是自己可以设置的颗粒度。

[array addObject:…] : 向可变数组尾部添加数据对象。

[array addObjectsFromArray:..] :向可变数组尾部添加一个数组对象。

[array removeObject:(id)] :删除数组中指定元素,根据对象 isEqual 消息判断。

[array removeObjectIdenticalTo:(id)] : 删除数组中指定元素,根据对象的地址判断

[array removeObjectIdenticalTo:(id) inRange:(NSRange)] : 在指定范围内删除指定的元素。

[array removeObjectsInArray:(NSArray *)] :删除一个数组的元素。

  • 索引遍历

    for (NSObject * object in muArray) {
      NSLog(@"数组对象:%@", object);
    }
    

Set

NSSet

NSSet 是 Objective-C 中的不可变集合。

        // 两种初始化方式
        NSSet *set1 = [[NSSet alloc] initWithObjects:@"1", @"2", @"3", nil]
        NSLog( @"%@", set1 )

        NSSet *set2 = [NSSet setWithObjects:@"12", @"23", @"34", nil]
        NSLog( @"%@", set2 )

        //用数组对象来创建集合对象
        NSArray *array = @[@1, @2, @2]
        //initWithArray 和 setWithArray 将数组对象转换成集合对象,这样能将数组中重复的对象过滤掉
        NSSet *set3 = [[NSSet alloc] initWithArray:array]
        NSLog( @"%@", set3 )
        NSSet *set4 = [NSSet setWithArray:array]
        NSLog( @"%@", set4 )

        //获取集合中对象的个数
        NSLog( @"%ld", [set4 count] )

        //获取集合中的对象(返回的是任意一个对象,如果集合中没有对象,则返回nil)
        id object1 = [set4 anyObject]
        NSLog( @"%@", object1 )

        //判断一个给定的对象是否包含在指定的集合中
        NSString *result1 = [set4 containsObject:@2] ? @"YES" : @"NO"
        NSLog( @"%@ is contained int set %@", @2, result1 )
//        @2  换成  @"2" 结果打印的是  NO
//        NSString *result1 = [set4 containsObject:@"2"] ? @"YES" : @"NO"
//        NSLog( @"%@ is contained int set %@", @"2", result1 )

NSMutableSet

NSMutableSet 是可变集合。

        //初始化
        NSMutableSet *mutableSet1 = [[NSMutableSet alloc] init]
        NSLog( @"%@", mutableSet1 )
        NSMutableSet *mutableSet2 = [NSMutableSet set]
        NSLog( @"%@", mutableSet2 )
        //通过不可变对象创建
        NSMutableSet *mutableSet3 = [[NSMutableSet alloc] initWithSet:set1]
        NSLog( @"%@", mutableSet3 )
        NSMutableSet *mutableSet4 = [NSMutableSet setWithSet: set1]
        NSLog( @"%@", mutableSet4 )

        //添加集合元素(注意:@4 和 @"4"不一样)
        [mutableSet4 addObject:@4]
        NSLog( @"%@", mutableSet4 )

        //删除单个集合元素
        [mutableSet4 removeObject:@4]
        NSLog( @"%@", mutableSet4 )

        //删除所有集合元素
        [mutableSet4 removeAllObjects]
        NSLog( @"%@", mutableSet4 )

NSCountedSet

NSCountedSet 是 NSSet 的子类,能记录集合中的元素的重复次数。

NSCountedSet *countSet1 = [NSCountedSet set]
        [countSet1 addObject:@1]
        [countSet1 addObject:@2]
        [countSet1 addObject:@3]
        [countSet1 addObject:@2]
        NSLog( @"%@", countSet1 )

        //单独获取某个对象在集合中出现过多少次
//        NSLog( @"%ld", [countSet1 countOfObjc:@3] )
        NSLog( @"%ld", [countSet1 countForObject:@5] )

流程控制

函数

代码块(Block)

代码块本质上是和其他变量类似。不同的是,代码块存储的数据是一个函数体。使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值。脱字符(^)是块的语法标记。按照我们熟悉的参数语法规约所定义的返回值以及块的主体(也就是可以执行的代码)。下图是如何把块变量赋值给一个变量的语法讲解:

类比而言,可以认为 Block 就是声明了一个匿名函数。

    void (^printBlock)(NSString *x);
    printBlock = ^(NSString* str)
    {
        NSLog(@"print:%@", str);
    };
    printBlock(@"hello world!");

一般来说,建议把某个代码块声明为变量类型进行使用:

typedef void (^printBlock)(NSString *x);

变量访问

TestBlock.h 与 TestBlock.m 的实现

//
//  TestBlock.m
//  BlockDemo
//
//  Created by fenglh on 14-8-18.
//  Copyright (c) 2014年 yons. All rights reserved.
//

#import "TestBlock.h"

@implementation TestBlock
@synthesize m_age;

//初始化
- (id) init:(int)age
{
    self = [super init];
    if (nil != self) {
        m_age = age;
    }
    return self;
}

- (void)AccessVarInBlock
{
    typedef void ( ^ MyBlock )(void);

    int outside_age=20;         //Block 外部的整形变量
    int *p_age = &outside_age;  //Block 外部的指针变量


    MyBlock aBlock = ^(){       //start Block
        NSLog(@" class member variable:\tm_age = %d", self.m_age);
        NSLog(@" outside variable: \t\toutside_age = %d", outside_age);
        NSLog(@" outside point variable:p_age = %d", *p_age);
    }; //end Block


    self.m_age = 100; //语句1
    *p_age = 100;     //语句2
    outside_age = 100;//语句3

    aBlock();

}

@end
  • main 函数
//
//  main.m
//  BlockDemo
//
//  Created by fenglh on 14-8-18.
//  Copyright (c) 2014年 yons. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "TestBlock.h"

int main(int argc, const char * argv[])
{
    TestBlock *test = [[TestBlock alloc ] init:10];
    [test AccessVarInBlock];

}

输出结果:

2014-08-18 23:40:35.334 BlockDemo[2136:303]  class member variable: m_age = 100
2014-08-18 23:40:35.336 BlockDemo[2136:303]  outside variable:      outside_age = 20
2014-08-18 23:40:35.337 BlockDemo[2136:303]  outside point variable:p_age = 100

先看,AccessVarInBlock 方法,如果把代码 1、2、3(即 aBlock(); 上面三句)都注释掉的话,其输出结果为:

2014-08-18 23:35:06.513 BlockDemo[2111:303]  class member variable: m_age = 10
2014-08-18 23:35:06.516 BlockDemo[2111:303]  outside variable:      outside_age = 20
2014-08-18 23:35:06.517 BlockDemo[2111:303]  outside point variable:p_age = 20

可得出以下结论:

  • 指针变量和类成员变量,在 Block 的内部实现中,是一种引用而非拷贝。
  • 其他基本类型的变量,在 Block 的内部实现中,是一种拷贝。

Block 机制

将以下 Objective-C 文件进行 Clang 编译:

    include <stdio.h>
    int main()
    {
     int i = 10;
     intint *p =&i;
     typedef void (^ typeBlock )(void);
        typeBlock aBlock = ^{
            printf("i = %d\n", i);
            printf("*p = %d", *p);
         }
        aBlock();
        return 0;
    }

可以发现:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int i;// 定义一个整形变量(并非引用)
  int *p; //定义了一个指针变量,也就是一个引用
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int *_p, int flags=0) : i(_i), p(_p) {//构造函数,赋值
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

可以发现:

1)原本的变量 i,在 block 的内部重新定义了一个 i 并且在该结构体的构造函数进行赋值,也就是对 i 进行了一份拷贝。

2)指针变量 p,在 block 内部也有一个变量 p,但是在构造函数的时候,它是指针地址的赋值,也就是说是一份引用。

3)可以猜测,类成员变量,在 block 的内部数据结构中也是类似一个指针的这样的实现!

类与对象

定义与实例化

Objective-C 类的定义中需要在.h 文件中定义@interface,在.m 文件中也存在有@interface 与@implementation。在.h 文件的@interface 中定义的属性是公有属性,可以被外部文件访问。而.m 文件的@interface 中定义的属性是私有属性,只可以被内部文件访问。譬如在.h 文件中:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

在.m 文件中:

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *referencedView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

属性

ObjectiveC 中的前世今生

在成员变量的管理上主要是依靠手动计数与 ARC 这两大机制,详情可以查看下文的 Advanced-内存管理这一章节。

基本的成员变量的声明方式为:

@property int age; // 默认是赋值
@property(retain) Book * book; // retain 参数实现内存管理

成员变量的相关属性有:

(1)内存管理的相关参数

参数名 说明
retain release 旧值,retain 新值
assign 直接赋值,默认就是这个,适用于非 OC 对象类型
copy release 旧值,copy 新值

retain 是指针拷贝,copy 是内容拷贝。需要注意的是,如果需要为某个类添加 int、bool 这样的非 object 的属性,应该使用 assign 参数,即:

@interface MyClass()

@property (assign) BOOL myBool;

@end

@implementation MyClass

@synthesize myBool = _myBool;  // creates an ivar myBool_ to back the property myBool.

@end

(2)是否要生成 set 方法

@property (readonly) int age; // 只读,只生成getter方法
@property (readwrite) int name; // 读写,默认是读写

(3)多线程管理

参数名 说明
nonautomic 性能高,
automic 性能低(默认)
  @property  (nonautomic, assign) int age; // 以后这样写,默认的assign也要写出来,这样比较明显
  @property  (setter = myAge:) int age; // setter = set方法名,自定义setter方法名,不要忘记冒号
  @property  (getter = getAge) int age; // getter = get方法名,自定义getter方法名
  @property  (getter = isRich) BOOL rich; // 一般这个会用在BOOL类型的变量声明,getter方法名以is开头

(4)指针类型

iOS 常见的指针类型有 strong 与 weak 两种,二者的区别可以借鉴于StackOverflow上的这篇文章,简而言之:weak 和 strong 不同的是 当一个对象不再有 strong 类型的指针指向它的时候 它会被释放,即使还有 weak 型指针指向它。一旦最后一个 strong 型指针离去,这个对象将被释放,所有剩余的 weak 型指针都将被清除。

可能有个例子形容是妥当的。想象我们的对象是一条狗,狗想要跑掉(被释放)。strong 型指针就像是栓住的狗。只要你用牵绳挂住狗,狗就不会跑掉。如果有 5 个人牵着一条狗(5 个 strong 型指针指向 1 个对象),除非 5 个牵绳都脱落,否着狗是不会跑掉的。weak 型指针就像是一个小孩指着狗喊到:“看!一只狗在那” 只要狗一直被栓着,小孩就能看到狗,(weak 指针)会一直指向它。只要狗的牵绳脱落,狗就会跑掉,不管有多少小孩在看着它。

只要最后一个 strong 型指针不再指向对象,那么对象就会被释放,同时所有的 weak 型指针都将会被清除。

(5)读写属性

Objective-C 语言关键词,@property 与@synthesize 配对使用。@synthesize 可以为某个属性方法。

延迟实例化

对于复杂数据类型的类的成员变量而言,往往面临着一个分配与实例化的要求。如果把这种实例化全部写入 init 方法中,无疑会导致极大的内存消耗。很多成员变量可能并不需要被实例化,OJC 提供了一种类似于 Getter/Setter 的方法来进行延迟实例化:

//.h
@property (nonatomic, strong) NSMutableArray* myArray;

//.m
@synthesize myArray = _myArray;//新版Xcode中自动会加上这句话

- (NSMutableArray*) myArray
{
  if (!_myArray){
    _myArray = [[NSMutableArray alloc] initWithCapacity:2];
  }
  return _myArray;
}

方法

Objective-C 中,调用方法被称为发送消息,通知对象执行某种操作。语法如下:[shape draw]

(1)类声明(接口):

一般公开方法的声明会放置在.h 文件的@interface 中,隐私方法的声明放在.m 文件的@interface 中。方法也可以不需要声明而直接实现。

@interface TestCoop : NSObject {
    int iMonth;
    int iYear;
    int iDay;
}

- (void) setYear: (int) iYear;
- (void) primalSetData: (int)iYear :(int)iMonth :(int)iDay;
- (void) setData: (int)Year iMonth:(int)iMonth iDay:(int)iDay;
- (void) displayDateInfo;

@end

1、前面的短线/- 表示这是 Objective-C 方法的声明,用来区分函数原型(C 语言中)与(Objective—C 中)方法声明的方式。短线后面是方法的返回类型/比如(void),位于圆括号中。方法名字前面的单个减号(-)表明该方法是一个实例方法。如果方法名字前面是一个加号(+),则表明该方法是一个类(static)方法。

1.1 注意,方法的声明在括号后面,@end 之前 {}区域里只有变量的定义,这点和 C++很不一样。

2、返回类型后面自然是函数名字,和 C 语言一样的,不同的是参数声明方法

2.1、无参数的函数后面不需要加括号和冒号,直接是函数名结束加分号,比如: - (void) displayDateInfo;

2.2、有参数的后面加冒号和参数类型名字,比如:

- (void) setDay: (int) iDay; //单个参数
- (void) primalSetData: (int)iYear :(int)iMonth :(int)iDay;//多个参数

objective 还提供一种中缀符的语法,方法的名称和参数都是和在一起的:

参数前面多加一个标识符,通常和变量名一样,如下:

- (void) setData: (int)Year iMonth:(int)iMonth iDay:(int)iDay;//多个参数

实例化

TestCoop *ptest = [TestCoop new];
TestCoop *ptest = [[TestCoop alloc] init];

苹果推荐使用第二种方法,虽然繁琐点。 在引入了 Swift 之后,如果要在 Objective-C 中调用含有参数的 Swift 的

单例模式

单例模式是常用的设计模式之一,有助于减少冗余代码。

+ (id)sharedInstance
{
  static dispatch_once_t pred = 0;
  __strong static id _sharedObject = nil;
  dispatch_once(&pred, ^{
    _sharedObject = [[self alloc] init]; // or some other init method
  });
  return _sharedObject;
}

匿名类

譬如id<NSObject>这样的就是代表一个匿名类。

Protocol & Delegate

Protocol 类似 C++中的抽象类和 Java 中的 interface。分为 Formal protocol 和 informal protocol 两种,其中在 Objective C 2.0 之前,informal protocol 是 Cocoa 中实现 delegate 的重要基础。因为 Objective C 2.0 引入了@optional 和@required 关键字。delegate 是一种常用的设计模式,而不是 Objective C 或者 Cocoa 的 feature,也没有任何关键字是给 delegate 用的。Delegate 其实表示的是一种代理模式,

举例而言,这叫好比我想买个手机,所以我有个 buyIphone 方法,但是我不知道谁那买手机,所以把这个需求发布出去(比如公布在网站上),如果有卖手机的商人(也就是说他能实现 buyIphone 这个方法)看到,他就会接受我的委托,(在商人自己的类中实现),那么我的委托对象就指向了这个商人..当我要买手机的时候,直接找他就行了.

@protocol MyDelegate
-(void)buyIphone:(NSString *)iphoneType money:(NSString *)money;

@end
@interface My : NSObject
{
    id<MyDelegate> deleage;
}
@property(assign,nonatomic)id<MyDelegate> delegate;
@end

代码中声明了一个协议 名叫 Mydelegate,在其中有一个 buyIphone 方法,即一个委托项。当我要购买手机的时候只需要通过 delegate 调用 BuyIphone 方法即可.

如下:

delegate = [[Business alloc] init]

-(void)willbuy
{
    [delegate buyIphone:@"iphone 4s" money:@"4888"];

}

例如:商人类实现了这一委托(用表示实现)

#import <Foundation/Foundation.h>
#import "My.h"
@interface Business : NSObject<MyDelegate>
@end

然后在 @implementation Business 中调用 buyIphone 方法

#import "Business.h"

@implementation Business

-(void)buyIphone:(NSString *)iphoneType money:(NSString *)money
{
    NSLog(@"手机有货,这个价钱卖你了,发货中!!");
}
@end

Advanced

内存管理

苹果的内存管理从早期的手动的引用计数到后面的 autoreleasepool,以及到现在的 ARC。

参考资料

自动释放池

ARC