追问系列:NSString *obj = [[NSData alloc] init];

NSString *obj = [[NSData alloc] init];
obj在编译时是NSString类型对象,运行时是NSData类型对象,怎么解释下理由?

编译时类型和运行时类型是两个不同的概念。

编译时类型是在代码编译阶段确定的,主要用于编译器检查我们的代码是否有类型相关的错误。在你的例子中,obj的编译时类型是NSString *,所以你可以对obj调用NSString的方法而不会在编译阶段报错。

运行时类型是在代码运行的时候确定的,主要决定了对象的行为。在你的例子中,obj实际上是一个NSData对象,所以运行时类型是NSData。

所以,虽然obj在编译时被视为NSString *类型,但是实际上它是一个NSData对象。这就意味着如果你试图对obj调用NSString的方法,虽然在编译阶段不会报错,但是在运行时会抛出异常,因为NSData并不实现NSString的方法。

这种情况通常是应该避免的,因为它可能会导致运行时错误。当你给一个变量赋值的时候,应该确保变量的类型和赋值对象的类型是一致的,这样才能确保代码的正确性。

追问:

  1. 在Objective-C中,编译时和运行时类型的不同可能会导致哪些问题?请举例说明。

  2. 请解释Objective-C中的动态类型(dynamic typing)和静态类型(static typing)。如何在代码中使用它们?

  3. 在Objective-C中,如果一个对象被声明为一个类,但实际上被初始化为另一个类,会发生什么?这会引发哪些运行时错误?

  4. 如何在Objective-C中通过代码检测一个对象的真实类型,而不仅仅是它声明的类型?

  5. 在Objective-C中,如何安全地处理类型不匹配的问题?如果一个变量被声明为一个类型,但被赋值为另一个类型,应该如何处理?

  6. 请解释Objective-C中的类簇(Class Cluster)概念,并解释它如何影响对象的运行时类型。

  7. 在Objective-C中,我们如何确保我们的代码在面对不同类型的对象时仍然能够正确运行(例如,使用多态)?

  8. 如何使用Objective-C的反射功能来查询和操作对象的运行时类型?

  9. 请解释Objective-C中的类型转换,包括强制类型转换和安全类型转换,以及它们可能带来的风险和问题。

追问回答:

追问1:

在Objective-C中,编译时和运行时类型的不同可能导致运行时错误。例如,如果你把一个NSData对象赋值给一个NSString变量,那么在运行时试图调用NSString的方法就会抛出异常。

追问2: 

静态类型是在编译时确定的,它决定了变量可以接受哪些类型的值。动态类型则是在运行时确定的,它决定了对象的行为。在Objective-C中,我们可以使用id类型来表示动态类型。

追问3:  

如果一个对象被声明为一个类,但实际上被初始化为另一个类,那么在运行时,如果试图调用该对象的方法,就可能会出错。因为实际的对象可能并不实现这些方法。

追问4:  

可以使用isKindOfClass:或者isMemberOfClass:方法来检测一个对象的真实类型。

追问5:  

可以使用isKindOfClass:或者isMemberOfClass:方法来检查一个对象是否是预期的类型。如果不是,可以抛出异常或者进行其他的错误处理。

追问6:  

类簇是一种设计模式,它隐藏了具体的子类,只暴露了一个公共的父类。例如,NSNumber和NSString都是类簇。当你创建一个NSNumber或NSString对象时,实际上创建的是它们的一个具体子类的实例。

追问7:  

可以使用多态来处理这种情况。多态允许我们把不同类型的对象赋值给同一种类型的变量,然后在运行时决定调用哪个方法。

追问8:  

可以使用Objective-C的反射功能,例如NSClassFromString、NSStringFromClass、NSSelectorFromString等方法,来查询和操作对象的运行时类型。

追问9:  

类型转换可以让我们把一个类型的变量转换为另一个类型。强制类型转换可能会导致未定义的行为,如果转换的类型并不匹配。安全类型转换(例如使用isKindOfClass:方法检查类型)可以避免这种问题。

// // TSSUserDefaults.m // TPSecureStorage // // Created by CaiXin on 2018/4/27. // Copyright © 2018年 CaiXin. All rights reserved. // #import "TSSUserDefaults.h" #import "TSSAESCrypt.h" static TSSUserDefaults* lUserDefaults = nil; static dispatch_once_t onceToken; NSString* const TSSUserDefaultsEncryptedObjectKeyPrefix = @"[TSSUserDefaults-Encrypted]-["; NSString* const TSSUserDefaultsEncryptedObjectKeySuffix = @"]-[TSSUserDefaults-Encrypted]"; NSString* const TSSUserDefaultsUnencryptedObjectKeyPrefix = @"[TSSUserDefaults-Unencrypted]-["; NSString* const TSSUserDefaultsUnencryptedObjectKeySuffix = @"]-[TSSUserDefaults-Unencrypted]"; @interface TSSUserDefaults () @property (nonatomic, readwrite, strong) NSUserDefaults* userDefaults; @end @implementation TSSUserDefaults + (instancetype)standardUserDefaults { dispatch_once(&onceToken, ^{ lUserDefaults = [[self alloc] initWithUserDefaults:[NSUserDefaults standardUserDefaults]]; }); return lUserDefaults; } - (instancetype)initWithUserDefaults:(NSUserDefaults*)userDefaults { if (userDefaults == nil) { return nil; } self = [super init]; if (self) { self.userDefaults = userDefaults; } return self; } - (BOOL)synchronize { return [self.userDefaults synchronize]; } - (NSString*)encryptedObjectKeyForKey:(NSString*)key { return [NSString stringWithFormat:@"%@%@%@", TSSUserDefaultsEncryptedObjectKeyPrefix, key, TSSUserDefaultsEncryptedObjectKeySuffix]; } - (NSString*)unencryptedObjectKeyForKey:(NSString*)key { return [NSString stringWithFormat:@"%@%@%@", TSSUserDefaultsUnencryptedObjectKeyPrefix, key, TSSUserDefaultsUnencryptedObjectKeySuffix]; } - (id)objectForKey:(NSString *)defaultName { if (defaultName == nil) { return nil; } id obj = [self.userDefaults objectForKey:defaultName]; if (obj) { [self setObject:obj forKey:defaultName]; } else { NSData* encryptedData = [self.userDefaults objectForKey:[self encryptedObjectKeyForKey:defaultName]]; //有加密value if (encryptedData) { obj = [[TSSAESCrypt defaultCrypt] objectDecryptFromData:encryptedData]; } else { obj = [self.userDefaults objectForKey:[self unencryptedObjectKeyForKey:defaultName]]; } } return obj; } - (void)setObject:(id)value forKey:(NSString *)defaultName { [self setObject:value forKey:defaultName encrypted:YES]; } - (void)setObject:(nullable id)value forKey:(NSString *)defaultName encrypted:(BOOL)encrypted { if (defaultName == nil) { return; } NSString* newDefaultName = encrypted ? [self encryptedObjectKeyForKey:defaultName] : [self unencryptedObjectKeyForKey:defaultName]; NSString *keyToRemove = encrypted ? [self unencryptedObjectKeyForKey:defaultName] : [self encryptedObjectKeyForKey:defaultName]; [self.userDefaults removeObjectForKey:keyToRemove]; [self.userDefaults removeObjectForKey:defaultName]; if (value == nil) { [self.userDefaults removeObjectForKey:newDefaultName]; return; } if (encrypted) { NSData* encryptedData = [[TSSAESCrypt defaultCrypt] encryptWithObject:value]; if (encryptedData) { [self.userDefaults setObject:encryptedData forKey:newDefaultName]; } } else { [self.userDefaults setObject:value forKey:newDefaultName]; } } - (void)removeObjectForKey:(NSString *)defaultName { [self setObject:nil forKey:defaultName]; } @end @implementation TSSUserDefaults (Extend) - (NSInteger)integerForKey:(NSString *)defaultName { NSObject* obj = [self objectForKey:defaultName]; if ([obj isKindOfClass:[NSNumber class]]) { return ((NSNumber*)obj).integerValue; } else if ([obj isKindOfClass:[NSString class]]) { return ((NSString*)obj).integerValue; } return 0; } - (float)floatForKey:(NSString *)defaultName { NSNumber* num = [self objectForKey:defaultName]; if ([num isKindOfClass:[NSNumber class]]) { return num.floatValue; } return 0; } - (double)doubleForKey:(NSString *)defaultName { NSNumber* num = [self objectForKey:defaultName]; if ([num isKindOfClass:[NSNumber class]]) { return num.doubleValue; } return 0; } - (BOOL)boolForKey:(NSString *)defaultName { NSNumber* num = [self objectForKey:defaultName]; if ([num isKindOfClass:[NSNumber class]]) { return num.boolValue; } return 0; } - (NSString *)stringForKey:(NSString *)defaultName { NSString* str = [self objectForKey:defaultName]; if ([str isKindOfClass:[NSString class]]) { return str; } return nil; } - (NSData *)dataForKey:(NSString *)defaultName { NSData* obj = [self objectForKey:defaultName]; if ([obj isKindOfClass:[NSData class]]) { return obj; } return nil; } - (NSArray *)arrayForKey:(NSString *)defaultName { NSArray* obj = [self objectForKey:defaultName]; if ([obj isKindOfClass:[NSArray class]]) { return obj; } return nil; } - (NSDictionary *)dictionaryForKey:(NSString *)defaultName { NSDictionary* obj = [self objectForKey:defaultName]; if ([obj isKindOfClass:[NSDictionary class]]) { return obj; } return nil; } - (NSURL *)URLForKey:(NSString *)defaultName { if (defaultName == nil) { return nil; } NSURL* obj = [self.userDefaults URLForKey:defaultName]; if (obj) { [self setObject:obj forKey:defaultName]; } else { obj = [self objectForKey:defaultName]; if (![obj isKindOfClass:[NSURL class]]) { obj = nil; } } return obj; } - (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName { [self setInteger:value forKey:defaultName encrypted:YES]; } - (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName encrypted:(BOOL)encrypted { [self setObject:@(value) forKey:defaultName encrypted:encrypted]; } - (void)setFloat:(float)value forKey:(NSString *)defaultName { [self setFloat:value forKey:defaultName encrypted:YES]; } - (void)setFloat:(float)value forKey:(NSString *)defaultName encrypted:(BOOL)encrypted { [self setObject:@(value) forKey:defaultName encrypted:encrypted]; } - (void)setDouble:(double)value forKey:(NSString *)defaultName { [self setDouble:value forKey:defaultName encrypted:YES]; } - (void)setDouble:(double)value forKey:(NSString *)defaultName encrypted:(BOOL)encrypted { [self setObject:@(value) forKey:defaultName encrypted:encrypted]; } - (void)setBool:(BOOL)value forKey:(NSString *)defaultName { [self setBool:value forKey:defaultName encrypted:YES]; } - (void)setBool:(BOOL)value forKey:(NSString *)defaultName encrypted:(BOOL)encrypted { [self setObject:@(value) forKey:defaultName encrypted:encrypted]; } - (void)setURL:(NSURL *)value forKey:(NSString *)defaultName { [self setURL:value forKey:defaultName encrypted:YES]; } - (void)setURL:(NSURL *)value forKey:(NSString *)defaultName encrypted:(BOOL)encrypted { [self setObject:value.copy forKey:defaultName encrypted:encrypted]; } @end extension TSSUserDefaults { func optionalBoolForKey(_ key: String) -> Bool? { let value = self.string(forKey: key) if value == nil { return nil } else if value == optionalBoolValueOn { return true } else { return false } } func setOptionalBool(key: String, value: Bool?) { if let value = value { self.setObject(value ? optionalBoolValueOn : optionalBoolValueOff, forKey: key) } else { self.setObject(nil, forKey: key) } } } 这是userdefaults的所有代码
09-19
解析这个m文件,并思考这个manager需要重点注意的地方 // // SDNDeviceDownloadImageManager.m // Omada // // Created by wanghao on 2025/2/12. // Copyright © 2025 TP-Link. All rights reserved. // #import "SDNDeviceDownloadImageManager.h" #import "TPDataStorage.h" #import "SDWebImageManager.h" #import "DMSDNDeviceImageInfo.h" NSString *const kDeviceDownloadFirstVersion = @"0"; static const NSInteger kSDNDeviceDownloadDefaultCacheMaxCacheAge = 365 * 24 * 60 * 60 * 50; // 50 years static const NSInteger kSDNDeviceDownloadDefaultCacheMinUpdateInterval = 3 * 24 * 60 * 60; // 3 days @interface SDNDeviceDownloadImageManager () @property (nonatomic, readwrite, strong) TPECFetchDeviceIconModule *deviceIconModule; @property (nonatomic, readwrite, strong) TPAbstractHandle *checkDeviceUpdateHandle; @property (nonatomic, readwrite, copy) NSArray<DMSDNDeviceImageInfo *> *updateDeviceImageList; @property (nonatomic, readwrite, strong) SDWebImageDownloader *imageDownloader; @property (nonatomic, readwrite, strong) SDImageCache *imageCache; @property (nonatomic, readwrite, assign) BOOL isDownloading; @end @implementation SDNDeviceDownloadImageManager + (instancetype)sharedInstance { static id instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { self.imageCache = [[SDImageCache alloc]initWithNamespace:@"OmadaDeviceStore"]; self.imageCache.maxCacheAge = kSDNDeviceDownloadDefaultCacheMaxCacheAge; self.imageDownloader = [SDWebImageDownloader sharedDownloader]; self.deviceIconModule = [ALAccountContext sharedInstance].eapCloudClient.fetchDeviceIconModule; self.updateDeviceImageList = [NSArray new]; self.isDownloading = NO; } return self; } - (TPAbstractHandle *)checkDeviceUpgrade { if (self.checkDeviceUpdateHandle) { return self.checkDeviceUpdateHandle; } NSString *currentVersion = [[TSSUserDefaults standardUserDefaults] objectForKey:TPDeviceIconCurrentVersionKey]; if (IsEmptyString(currentVersion)) { currentVersion = kDeviceDownloadFirstVersion; } TPWeakSelf TPQueueHandleFactoryBlock blck1 = ^TPAbstractHandle* (TPQueueHandleBlockContext* context){ TPStrongSelfNoReturn return [_self.deviceIconModule checkNeedUpgradeWithCurrentVersion:currentVersion]; }; TPQueueHandleFactoryBlock blck2 = ^TPAbstractHandle* (TPQueueHandleBlockContext* context){ TPStrongSelfNoReturn if(context.lastResult.success) { BOOL needCheckUp = _self.deviceIconModule.needUpdate; if (needCheckUp) { return [_self.deviceIconModule getDeviceIconWithCurrentVersion:currentVersion]; } } context.stop = YES; context.stopResult = context.lastResult; return nil; }; TPGCDQueueHandle *queueHandle = [[TPGCDQueueHandle alloc] initWithQueue:dispatch_get_main_queue() factoryBlockArray:@[blck1, blck2] timeout:-1]; [queueHandle addCompletionOnMainThread:^(TPHandleResult * _Nonnull result) { TPStrongSelf _self.checkDeviceUpdateHandle = nil; if (result.success) { _self.updateDeviceImageList = _self.deviceIconModule.updateDeviceImageList; if (_self.updateDeviceImageList.count > 0 && _self.deviceIconModule.needUpdate) { [_self downLoadUpdatedDeviceImage]; } } }]; return queueHandle; } - (void)updateCacheInfo { NSTimeInterval lastTime = 0.0; NSNumber *lastShowTimestampObject = [[TSSUserDefaults standardUserDefaults] objectForKey:TPDeviceIconLastUpdateTimeKey]; if (lastShowTimestampObject) { lastTime = [lastShowTimestampObject doubleValue]; } //上次更新时间距离现在超过最小间隔,则重新拉取设备图标 if (lastTime + kSDNDeviceDownloadDefaultCacheMinUpdateInterval < [[NSDate date] timeIntervalSince1970] && !self.isDownloading) { self.isDownloading = YES; [self resetCacheInfo]; [self checkDeviceUpgrade]; } } - (void)resetCacheInfo { [[TSSUserDefaults standardUserDefaults] setObject:kDeviceDownloadFirstVersion forKey:TPDeviceIconCurrentVersionKey]; [[TSSUserDefaults standardUserDefaults] setObject:[NSArray new] forKey:TPDeviceIconMdelListKey]; } - (void)storeDeviceModelList:(NSString *)modelAndVersionKey { if (IsEmptyString(modelAndVersionKey)) { return; } NSArray<NSString *> *imageList = [self deviceIconModelList]; for (NSString *itemImage in imageList) { if ([modelAndVersionKey isEqualToString:itemImage]) { return; } } imageList = [imageList arrayByAddingObject:modelAndVersionKey]; [[TSSUserDefaults standardUserDefaults] setObject:imageList forKey:TPDeviceIconMdelListKey]; } - (void)downLoadUpdatedDeviceImage { self.isDownloading = YES; __block int handledCount = 0; for (DMSDNDeviceImageInfo *deviceImageInfo in self.updateDeviceImageList) { if ([self.imageCache diskImageExistsWithKey:deviceImageInfo.modelAndVersionKey] || deviceImageInfo.imageDownLoadURL == nil) { [self storeDeviceModelList:deviceImageInfo.modelAndVersionKey]; handledCount++; [self updateControllerVersionWithTotalCount:handledCount]; continue; } TPWeakSelf [self.imageDownloader downloadImageWithURL:deviceImageInfo.imageDownLoadURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { TPStrongSelf handledCount++; [_self.imageCache storeImage:image recalculateFromImage:NO imageData:data forKey:deviceImageInfo.modelAndVersionKey toDisk:YES]; [self storeDeviceModelList:deviceImageInfo.modelAndVersionKey]; [[TSSUserDefaults standardUserDefaults] setObject:@([[NSDate date] timeIntervalSince1970]) forKey:TPDeviceIconLastUpdateTimeKey]; [_self updateControllerVersionWithTotalCount:handledCount]; }]; } } - (void)updateControllerVersionWithTotalCount:(int)totalCount { if (totalCount == self.updateDeviceImageList.count) { NSString *curVersion = [ALAccountContext sharedInstance].eapCloudClient.fetchDeviceIconModule.controllerVersion; [[TSSUserDefaults standardUserDefaults] setObject:curVersion forKey:TPDeviceIconCurrentVersionKey]; } } - (BOOL)hasDownLoadDevice:(NSString *)modelAndVersionKey { if (IsEmptyString(modelAndVersionKey)) { return NO; } return [self.imageCache diskImageExistsWithKey:modelAndVersionKey];; } - (UIImage *)deviceImage:(NSString *)deviceModel andModelVersion:(NSString *)modelVersion { NSString *modelAndVersionKey = [DMSDNDeviceImageInfo modelAndVersionKeyFromModel:deviceModel andVersion:modelVersion]; if (IsEmptyString(modelAndVersionKey)) { return nil; } UIImage *image = [self deviceImageFromModelAndVersionKey:modelAndVersionKey]; UIImage *finalImage = image == nil ? [self vigiDeviceImage:modelAndVersionKey] : image; if (finalImage == nil) { [self updateCacheInfo]; } return finalImage; } - (UIImage *)deviceImageFromModelAndVersionKey:(NSString *)modelAndVersionKey { UIImage *image = [self.imageCache imageFromDiskCacheForKey:modelAndVersionKey]; return image; } - (NSArray<NSString *> *)deviceIconModelList { NSArray<NSString *> *deviceModelList = [[TSSUserDefaults standardUserDefaults] objectForKey:TPDeviceIconMdelListKey]; return deviceModelList == nil ? [NSArray new] : deviceModelList; } - (UIImage *)vigiDeviceImage:(NSString *)modelAndVersionKey { NSArray<NSString *> *modelList = [self deviceIconModelList]; modelList = [modelList sortedArrayUsingComparator:^NSComparisonResult(NSString * obj1, NSString * obj2) { return obj1.length < obj2.length; }]; for (NSString *modelStr in modelList) { if (!IsEmptyString(modelAndVersionKey) && [modelAndVersionKey containsString:modelStr]) { return [self deviceImageFromModelAndVersionKey:modelStr]; } } return nil; } @end
10-31
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值