1.NSString
字符串的初始化
? ? ? ? NSString *str1 = [[NSString alloc]init];
? ? ? ? NSString *str2 = @"abc";
字符串和基本數(shù)據(jù)類型轉(zhuǎn)化
? ? ? ? 基礎(chǔ)數(shù)據(jù)類型? ->? 字符串
? ? ? ? ? ? ? ? NSString *str3 = [NSString stringWithFormat:@"%d",1] ;
? ? ? ? NSString *str4 = [NSString stringWithFormat:@"%f",1.5];
? ? ? ? 字符串? ->基礎(chǔ)數(shù)據(jù)類型
? ? ? ? int a = str3.intValue;
? ? ? ? float b = str4.floatValue;
字符串比較
? ? ? ? NSString *str1 = [NSString stringWithFormat:@"%@",@"abc"];
? ? ? ? NSString *str2 = [NSString stringWithFormat:@"%@",@"abc"];
? ? ? ? NSString *str3 = [NSString stringWithFormat:@"%@",@"123"];
? ? ? ? if ([str1 isEqualToString:str2]) {
? ? ? ? ? ? NSLog(@"字符串相等");
? ? ? ? }
? ? ? ? if (![str1 isEqualToString:str3]) {
? ? ? ? ? ? NSLog(@"字符串不相等");
? ? ? ? }
? ? ? ? 注意:if ([str1 == str2])不用這種寫法饱狂,==意味著內(nèi)容和內(nèi)存地址都要相等宣决。
字符串大小寫
? ? ? ? NSString *str1 = @"abcDefg";
? ? ? ? NSLog(@"%@",[str1 lowercaseString]);? ? //? 全部小寫abcdefg
? ? ? ? NSLog(@"%@",[str1 uppercaseString]);? ? //? 全部大寫ABCDEFG
? ? ? ? NSLog(@"%@",[str1 capitalizedString]);? //? 首字母大寫Abcdefg
字符串內(nèi)容索引
? ? ? NSString *str1 = @"abcdefg";
? ? ? ? NSString *str2 = @"cde";
? ? ? ? NSRange range;? //結(jié)構(gòu)體,描述的是字符串的長度和位置
? ? ? ? range = [str1 rangeOfString:str2];? ? //位置從0開始算
? ? ? ? NSLog(@"%lu %lu",(unsigned long)range.length,(unsigned long)range.location);
? ? ? ? 輸出結(jié)果:3 2
字符串的位置索引
? ? ? ? NSString *str1 = @"abcdefg";
? ? ? ? NSRange range;
? ? ? ? range.length = 2;
? ? ? ? range.location = 3;
? ? ? ? NSString *str2 = [str1 substringWithRange:range];
? ? ? ? NSLog(@"%@",str2);
? ? ? ? 輸出結(jié)果:de
字符串增刪改查
? ? ? ? NSString *str1 = @"abc";
? ? ? ? NSString *str2 = [str1 stringByAppendingString:@"123"]; //字符串追加
? ? ? ? NSString *str3 = [str2 stringByAppendingFormat:@"%d",999];? //字符串追加
? ? ? ? NSString *str4 = [str3 stringByReplacingOccurrencesOfString:@"c123" withString:@"aa"];? //字符串? ? ? ? ? ? ? 修改
? ? ? ? NSString *str5 = [str3 stringByReplacingOccurrencesOfString:@"999" withString:@""];? //字符串刪除? ?
可變字符串
? ? ? ? NSMutableString *str6 = [[NSMutableString alloc]init];
? ? ? ? [str6 appendString:@"abc"];
? ? ? ? [str6 appendString:@"123"];
? ? ? ? [str6 insertString:@"hhh" atIndex:3];
? ? ? ? [str6 appendFormat:@"%f",4.5];
? ? ? ? NSLog(@"%@",str6);
? ? ? ? 輸出結(jié)果:abchhh1234.500000
字符串檢查
? ? ? if ([str1 containsString:@"imooc"]) {
? ? ? ? ? ? NSLog(@"包含慕課網(wǎng)");
? ? ? ? }
? ? ? ? if ([str1 hasPrefix:@"http"]) {
? ? ? ? ? ? NSLog(@"http");
? ? ? ? }
? ? ? ? if ([str1 hasSuffix:@"com"]) {
? ? ? ? ? ? NSLog(@"com");
? ? ? ? }
常用技巧
? ? ? ? NSString *str1 = @" abc edf ";
? ? ? ? NSLog(@"str1 = %@",str1);
? ? ? ? NSString *str2 = [str1 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];//首尾兩端去空格
? ? ? ? NSLog(@"str2 = %@",str2);
? ? ? ? NSLog(@"%d %d",str1.length,str2.length);
輸出結(jié)果:str1 =? abc edf
? ? ? ? ? ? ? ? ? ? str2 = abc edf
? ? ? ? ? ? ? ? ? ? 9 7
2.數(shù)組NSArray
OC中全局NSMutableArray對象不能addObject局部對象U煨ァ!紊浩!
解決方法https://blog.csdn.net/ttf1993/article/details/46470897
數(shù)組的初始化
? ? ? ? NSArray *array1 = [[NSArray alloc]init];
? ? ? ? NSArray *array2 = [[NSArray alloc]initWithObjects:@"abc",@"123",@"d",nil];
? ? ? ? NSArray *array3 = @[@"333",@"abc"];
增刪改查
? ? ? ? //查:位置索引
? ? ? ? NSString *item1 = [array2 firstObject];
? ? ? ? NSString *item2 = [array2 lastObject];
? ? ? ? NSString *item3 = [array2 objectAtIndex:1];
? ? ? ? NSLog(@"%@ %@ %@",item1,item2,item3);
? ? ? ? //查:內(nèi)容索引
? ? ? ? NSInteger index1 = [array2 indexOfObject:@"d"];
? ? ? ? NSLog(@"index = %ld",(long)index1);
? ? ? ? //查:長度
? ? ? ? NSLog(@"count = %lu",(unsigned long)array2.count);
? ? ? ? //查:是否包含
? ? ? ? NSLog(@"%d %d",[array2 containsObject:@"123"],[array2 containsObject:@"kj"]);
? ? ? ? //增:
? ? ? ? NSMutableArray *array1 = [[NSMutableArray alloc]init];
? ? ? ? NSMutableArray *array2 = [[NSMutableArray alloc]initWithObjects:@"abc",@"123",@"ff", nil];
? ? ? ? [array2 addObject:@"456"];
? ? ? ? [array2 insertObject:@"00" atIndex:2];
? ? ? ? //刪除 修改
? ? ? ? [array2 removeLastObject];
? ? ? ? [array2 removeObject:@"00"];
? ? ? ? [array2 removeObjectAtIndex:1];
? ? ? ? [array2 removeAllObjects];
數(shù)組遍歷和快速枚舉:含中文
? ? ? ? NSArray *array1 = @[@"abc",@"123",@"你好"];//數(shù)組中直接存儲中文輸出會(huì)亂碼
? ? ? ? NSLog(@"%@",array1);
? ? ? ? for (int i = 0; i < array1.count; i++) {//出現(xiàn)中文需要打印可以使用for循環(huán)
? ? ? ? ? ? NSString *item = [array1 objectAtIndex:i];
? ? ? ? ? ? NSLog(@"item = %@",item);
? ? ? ? }
? ? ? ? for (NSString *item in array1) {//出現(xiàn)中文也可以使用快速枚舉
? ? ? ? ? ? NSLog(@"item = %@",item);
? ? ? ? }
字符串?dāng)?shù)組相互轉(zhuǎn)化
? ? ? ? //NSString <-> NSArray
? ? ? ? NSString *str = @"1@2@3@4@5";
? ? ? ? NSArray *array1 = [str componentsSeparatedByString:@"@"];//表明按照@進(jìn)行劃分
? ? ? ? NSLog(@"%@",array1);
? ? ? ? NSMutableString *str1 = [[NSMutableString alloc]init];
? ? ? ? for (NSString *item in array1) {
? ? ? ? ? ? [str1 appendFormat:@"%@@",item];? ? ? ? //第二個(gè)@只是一種格式蔫饰,之前就是用這個(gè)@進(jìn)行劃分的
? ? ? ? }
? ? ? ? NSLog(@"%@",str1);
可變數(shù)組的使用
? ? ? ? ? ? ? ? NSArray *array1 = @[@"123",@"abc"];
//? ? ? NSMutableArray *array2 = array1;? //NSArray -> NSMutableArray不要用這種方式
? ? ? ? NSMutableArray *array2 = [[NSMutableArray alloc]initWithArray:array1];? //用這種
? ? ? ? NSArray *array3 = array2;? //NSMutableArray -> NSArray可以直接用這種
3.字典NSDictionary
1 vs 1
key:唯一
無順序
字典初始化
? ? ? ? NSDictionary *dict1 = [[NSDictionary alloc]init];
? ? ? ? NSDictionary *dict2 = [[NSDictionary? alloc] initWithObjectsAndKeys:@"2.5",@"apple",@"2.2",@"orange", nil];? ? ? ? //這種是先value,后面接key
? ? ? ? NSDictionary *dict3 = @{@"1.64":@"beijing",@"1.19":@"tianjin"};? ? //這種是key:value
增刪改查
? ? ? ? //查:key索引 一般通過key查value,因?yàn)閗ey唯一而value不唯一
? ? ? ? NSString *str1 = [dict2 valueForKey:@"apple"];
? ? ? ? NSLog(@"%@",str1);
? ? ? ? //查所有key 所有value榔幸,查了之后會(huì)以數(shù)組的形式返回
? ? ? ? NSArray *array1 = [dict2 allKeys];
? ? ? ? NSArray *array2 = [dict2 allValues];
? ? ? ? NSLog(@"%@ %@",array1,array2);
? ? ? ? //查:遍歷 快速枚舉
? ? ? ? for (int i = 0; i < array1.count; i++) {? //value的枚舉
? ? ? ? ? ? NSString *item = [dict2 valueForKey:[array1 objectAtIndex:i]];
? ? ? ? ? ? NSLog(@"item = %@",item);
? ? ? ? }
? ? ? ? for (NSString *item in dict2) { //key的枚舉
? ? ? ? ? ? NSLog(@"item = %@",item);
? ? ? ? }
? ? ? ? //查:字典數(shù)組混合遍歷 沒有先后順序允乐,順序可能不同,對于內(nèi)容不統(tǒng)一的有字符串有數(shù)組的可以采用id形式
? ? ? ? NSArray *array3 = @[@"abc",@"123"];
? ? ? ? NSDictionary *dict4 = [[NSDictionary alloc]initWithObjectsAndKeys:@"value1",@"keya",@"value2",@"keyb",array3,@"keyc", nil];
? ? ? ? NSLog(@"%@",dict4);
? ? ? ? for (int i = 0; i < [dict4 allKeys].count; i++) {
? ? ? ? ? ? id item = [dict4 valueForKey:[[dict4 allKeys] objectAtIndex:i]];
? ? ? ? ? ? NSLog(@"item = %@",item);
? ? ? ? }
? ? ? ? //增
? ? ? ? NSMutableDictionary *mdict1 = [[NSMutableDictionary alloc]init];
? ? ? ? [mdict1 setValue:@"3" forKey:@"apple"];
? ? ? ? [mdict1 setValue:@"2.5" forKey:@"banana"];
? ? ? ? NSLog(@"%@",mdict1);
? ? ? ? //改
? ? ? ? [mdict1 setValue:@"8" forKey:@"apple"];
? ? ? ? NSLog(@"%@",mdict1);
? ? ? ? //刪
? ? ? ? [mdict1 removeObjectForKey:@"apple"];
? ? ? ? NSLog(@"%@",mdict1);
? ? ? ? [mdict1 removeAllObjects];
? ? ? ? NSLog(@"%@",mdict1);
? ? ? ? //中文
? ? ? ? NSDictionary *dict5 = @{@"北京":@"首都"};
? ? ? ? NSLog(@"%@",dict5); //亂碼
? ? ? ? NSString *item = [dict5 valueForKey:@"北京"];
? ? ? ? NSLog(@"%@",item);? //不亂碼
4.iOS內(nèi)存管理(非常重要削咆,面試時(shí)候可能會(huì)考)
內(nèi)存牍疏,指針,堆和棧的基本概念
存儲器拨齐,存儲器通常用來存儲程序和數(shù)據(jù)鳞陨,電腦的存儲空間=內(nèi)存條+硬盤,我們通常所說的電腦的內(nèi)存就是電腦的內(nèi)存條瞻惋,而手機(jī)的內(nèi)存是手機(jī)的內(nèi)存條+外部存儲空間
堆和棧的區(qū)別:堆由程序員手動(dòng)分配和釋放厦滤,棧由操作系統(tǒng)分配和釋放援岩,比如說一些局部變量可能存儲在棧中,堆:先進(jìn)先出掏导,棧:先進(jìn)后出
iOS內(nèi)存管理機(jī)制
電腦的硬盤相當(dāng)于手機(jī)的存儲空間享怀,電腦的內(nèi)存條相當(dāng)于手機(jī)的內(nèi)存。應(yīng)用下載后會(huì)占用手機(jī)的存儲空間趟咆,而應(yīng)用未啟動(dòng)的時(shí)候不占用內(nèi)存空間添瓷,啟動(dòng)之后才會(huì)占用內(nèi)存。
為什么要進(jìn)行內(nèi)存管理及哪些需要內(nèi)存管理忍啸?①所有繼承NSObject的子類都需要進(jìn)行內(nèi)存管理仰坦。? ? ②使用其他語言申請的內(nèi)存空間也需要進(jìn)行內(nèi)存管理,比如c 或c++進(jìn)行混合編程计雌。? ? ③對于int悄晃,BOOL,char這種基礎(chǔ)的數(shù)據(jù)類型不需要進(jìn)行內(nèi)存管理凿滤。? ? ④內(nèi)存警告:如果占用太多內(nèi)存妈橄,ios系統(tǒng)可能直接把應(yīng)用kill掉
對象存儲的基本結(jié)構(gòu):分兩個(gè)部分:①引用計(jì)數(shù)器:是一個(gè)整數(shù),表示對象被引用的次數(shù)翁脆,每一個(gè)OC對象的引用計(jì)數(shù)器都包含了4個(gè)字節(jié)? ? ②除引用計(jì)數(shù)器之外的部分眷蚓,包含類的指針,索引等
引用計(jì)數(shù)器的作用:
引用計(jì)數(shù)器幾個(gè)原則:①引用計(jì)數(shù)器初始值為0反番,所有的對象第一次創(chuàng)建的時(shí)候沙热,注意:只是創(chuàng)建,而未進(jìn)行其他操作罢缸,這個(gè)時(shí)候引用計(jì)數(shù)器為0? ? ②如果創(chuàng)建同時(shí)對它進(jìn)行一些操作如alloc new copy 引用計(jì)數(shù)器+1 篙贸,init只是初始化一些屬性,不加1? ③引用計(jì)數(shù)器為0內(nèi)存釋放? ? ④retain引用計(jì)數(shù)器+1? ? ⑤release的時(shí)候 引用計(jì)數(shù)器-1? ? ⑥如果想獲取引用計(jì)數(shù)器的值枫疆,可以通過retainCount爵川,不過一般使用較少
對象被銷毀原則:①引用計(jì)數(shù)器為0 ? ?②對象被銷毀調(diào)用dealloc,不要主動(dòng)調(diào)用dealloc方法息楔,這個(gè)方法是被動(dòng)調(diào)用的 ? ?③如果堅(jiān)持使用這塊已經(jīng)無法使用的內(nèi)存寝贡,就可能造成野指針:指向了一個(gè)不可用的內(nèi)存空間:崩潰 ? ?④空指針:表示這個(gè)指針指向的內(nèi)存為空,但它仍然合法值依,只是無法使用
ARC下內(nèi)存管理
?如果沒有強(qiáng)指針指向圃泡,那么對象就會(huì)被銷毀。所有的指針(我們沒有特殊定義的)都是強(qiáng)指針愿险。
-(void)dealloc{
? ? NSLog(@"我被釋放了");
}
//Person *p1 = [[Person alloc]init];
//NSLog(@"xx");
//? xx-》我被釋放了
//Person *p1 = [[Person alloc]init];
//p1 = nil;
//NSLog(@"xx");
// 我被釋放了->xx
//Person *p1 = [[Person alloc]init];
//Person *p2 = p1;
//p1 = nil;
//NSLog(@"xx");
//? xx-》我被釋放了
內(nèi)存自動(dòng)釋放池@autoreleasepool
概念:是oc中一種內(nèi)存自動(dòng)回收機(jī)制颇蜡,自動(dòng)釋放池執(zhí)行完的時(shí)候,池里的對象會(huì)做一次release
Runtime的理解(!重要)
Runtime概念:Runtime簡稱運(yùn)行時(shí)澡匪。就是在系統(tǒng)運(yùn)行的時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制褒链。
[person1 dosomething];
消息機(jī)制
person1_msgSend(obj,@selector(dosomething));
①將正常調(diào)用的方法翻譯成這種消息發(fā)送唁情。 ? ?②可以通過obj的isa指針,找到class ? ?③在cache中尋找dosomething方法是否存在甫匹,如果沒有找到執(zhí)行④ ? ?④在method_list中找甸鸟,在方法列表中找到之后兵迅,它會(huì)執(zhí)行這個(gè)方法抢韭,然后把這個(gè)方法緩存到本地的cache中
Runtime使用的場景:①程序運(yùn)行過程中,動(dòng)態(tài)地為程序添加(修改)某些方法和屬性 ? ?②遍歷類的所有方法和屬性
RunLoop的理解
RunLoop就是一個(gè)事件處理的循環(huán)恍箭,用來不停的調(diào)動(dòng)工作以及處理輸入事件刻恭。使用RunLoop的目的就是節(jié)省CPU效率,使線程在有工作的時(shí)候忙于工作扯夭,而沒工作的時(shí)候就處于休眠狀態(tài)
不停地接收數(shù)據(jù):RunLoop核心價(jià)值
5.深拷貝 淺拷貝
淺拷貝:他們指向的內(nèi)存空間一致鳍贾,簡單地copy指針,數(shù)據(jù)不獨(dú)立交洗,如果修改了A1骑科,A2也會(huì)跟著改變。當(dāng)我們通過A1對這片內(nèi)存空間進(jìn)行釋放的時(shí)候构拳,A2指向的內(nèi)存也不可用了
深拷貝:開辟一片新的內(nèi)存空間咆爽,拷貝內(nèi)容,A1置森,A2指向不同的內(nèi)存空間斗埂,當(dāng)我們通過A1對這片內(nèi)存空間進(jìn)行釋放的時(shí)候,對A2指向的內(nèi)存沒有任何影響暇藏,所以釋放對象的時(shí)候使用深拷貝
對象的快速拷貝
//main.m
????????Person *p1 = [[Person alloc]init];
? ? ? ? p1.name = @"Robin";
? ? ? ? Person *p2 = [p1 copy];
? ? ? ? NSLog(@"%@",p2.name);
? ? ? ? NSLog(@"%@ %@",p1,p2);? //輸出的內(nèi)存空間不同蜜笤,是深拷貝
//Person.h
????????@property(nonatomic,copy) NSString *name;
//Person.m
????-(id)copyWithZone:(NSZone*)zone{
? ? //創(chuàng)建一個(gè)新的內(nèi)存空間,并且將原來內(nèi)存空間的內(nèi)容拷貝一份
? ? Person *copy = [[[self class]allocWithZone:zone] init];
? ? copy.name = [self.name copyWithZone:zone];
? ? return copy;
}
可變地址空間和不可變地址空間的轉(zhuǎn)化
????????//不可變到可變 copy:只復(fù)制內(nèi)容 mutableCopy:修改地址
? ? ? ? NSString *str = @"Hello";
? ? ? ? NSMutableString *mstr = [str copy];
? ? ? ? NSMutableString *mstr2 = [str mutableCopy];
? ? ? ? NSLog(@"%@ %@ %@",str,mstr,mstr2);
? ? ? ? NSLog(@"%p %p %p",str,mstr,mstr2);? //%p是打印地址盐碱,str mstr地址相同把兔,mstr2地址不同
????????//不可變到不可變 copy:只復(fù)制內(nèi)容 mutableCopy:修改地址
? ? ? ? NSString *str = @"Hello";
? ? ? ? NSString *mstr = [str copy];
? ? ? ? NSString *mstr2 = [str mutableCopy];
? ? ? ? NSLog(@"%@ %@ %@",str,mstr,mstr2);
? ? ? ? NSLog(@"%p %p %p",str,mstr,mstr2);? //%p是打印地址,str mstr地址相同瓮顽,mstr2地址不同????
????????//可變到不可變 copy:只復(fù)制內(nèi)容 mutableCopy:修改地址
? ? ? ? NSMutableString *str = @"Hello";
? ? ? ? NSString *mstr = [str copy];
? ? ? ? NSString *mstr2 = [str mutableCopy];
? ? ? ? NSLog(@"%@ %@ %@",str,mstr,mstr2);
? ? ? ? NSLog(@"%p %p %p",str,mstr,mstr2);? //%p是打印地址县好,str mstr地址相同,mstr2地址不同
????????//可變到可變 copy:只復(fù)制內(nèi)容 mutableCopy:修改地址
? ? ? ? NSMutableString *str = @"Hello";
? ? ? ? NSMutableString *mstr = [str copy];
? ? ? ? NSMutableString *mstr2 = [str mutableCopy];
? ? ? ? NSLog(@"%@ %@ %@",str,mstr,mstr2);
? ? ? ? NSLog(@"%p %p %p",str,mstr,mstr2);? //%p是打印地址暖混,str mstr地址相同缕贡,mstr2地址不同
6.謂詞
????????//對數(shù)組中的元素進(jìn)行過濾
? ? ? ? NSArray *array1 = [[NSArray alloc]initWithObjects:@"ios",@"android",@"wp", nil];
? ? ? ? //1.創(chuàng)建一個(gè)謂詞對象? ? 2.指定過濾條件? ? 3.過濾條件應(yīng)用到對象上
? ? ? ? NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"SELF == 'android'"]; //使用單引號的原因是外面有雙引號
? ? ? ? NSArray *array2 = [array1 filteredArrayUsingPredicate:pre1];
? ? ? ? for (NSString *item in array2) {
? ? ? ? ? ? NSLog(@"item = %@",item);
? ? ? ? }
????????//邏輯判斷
? ? ? ? NSString *num1 = [[NSString alloc]initWithFormat:@"%d",3];
? ? ? ? NSString *num2 = [[NSString alloc]initWithFormat:@"%d",15];
? ? ? ? NSArray *array1 = [NSArray arrayWithObjects:num1,num2, nil];
? ? ? ? NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"SELF.intValue > 10"];
? ? ? ? NSArray *array2 = [array1 filteredArrayUsingPredicate:pre1];//返回值:數(shù)組array 參數(shù):謂詞Predicate
? ? ? ? for (NSString *item in array2) {
? ? ? ? ? ? NSLog(@"item = %@",item);
? ? ? ? }
? ??????//關(guān)鍵詞的使用 判斷數(shù)組中是否包含某個(gè)元素
? ? ? ? NSArray *array1 = [[NSArray alloc]initWithObjects:@"abc",@"def", nil];
? ? ? ? NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@",@"def"];
? ? ? ? NSArray *array2 = [array1 filteredArrayUsingPredicate:pre1];
? ? ? ? for (NSString *item in array2) {
? ? ? ? ? ? NSLog(@"item = %@",item);
? ??????????//注:輸出結(jié)果為item = def,如果@"SELF CONTAINS %@",@"def"改為@"SELF CONTAINS %@",@"de",輸出結(jié)果不變,仍是item = def
? ? ? ? ? ? //CONTAINS:檢查是否包含某個(gè)內(nèi)容
? ? ? ? }
????????//過濾對象
? ? ? ? Person *person1 = [[Person alloc]init];
? ? ? ? person1.age = 10;
? ? ? ? Person *person2 = [[Person alloc]init];
? ? ? ? person2.age = 20;
? ? ? ? NSMutableArray *array1 = [[NSMutableArray alloc]initWithObjects:person1,person2, nil];
? ? ? ? NSLog(@"%p %p",person1,person2);
? ? ? ? NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"age == %d",10];
? ? ? ? NSArray *array2 = [array1 filteredArrayUsingPredicate:pre1];//返回值:數(shù)組array 參數(shù):謂詞Predicate
? ? ? ? for (Person *p in array2) {
? ? ? ? ? ? NSLog(@"%p",p);
? ? ? ? }