38 changed files with 6158 additions and 6 deletions
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"originHash" : "edf512815619ed6bb2e67b3bdb38a134d5e8f6b3f3f79c05028ab9cbffc0efd2", |
||||
|
"pins" : [ |
||||
|
{ |
||||
|
"identity" : "fmdb", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/ccgus/fmdb", |
||||
|
"state" : { |
||||
|
"revision" : "1227a3fa2b9916bfd75fe380eb45cd210e69e251", |
||||
|
"version" : "2.7.12" |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"version" : 3 |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
FOUNDATION_EXPORT const long long SECONDS_IN_YEAR; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_28; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_29; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_30; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_31; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_WEEK; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_DAY; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_HOUR; |
||||
|
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MINUTE; |
||||
|
FOUNDATION_EXPORT const NSInteger MILLISECONDS_IN_DAY; |
||||
|
#import "DTError.h" |
@ -0,0 +1,33 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York |
||||
|
// |
||||
|
// Permission is hereby granted, free of charge, to any |
||||
|
// person obtaining a copy of this software and |
||||
|
// associated documentation files (the "Software"), to |
||||
|
// deal in the Software without restriction, including |
||||
|
// without limitation the rights to use, copy, modify, merge, |
||||
|
// publish, distribute, sublicense, and/or sell copies of the |
||||
|
// Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
// |
||||
|
// The above copyright notice and this permission notice shall |
||||
|
// be included in all copies or substantial portions of the Software. |
||||
|
// |
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
|
||||
|
#import "DTConstants.h" |
||||
|
const long long SECONDS_IN_YEAR = 31556900; |
||||
|
const NSInteger SECONDS_IN_MONTH_28 = 2419200; |
||||
|
const NSInteger SECONDS_IN_MONTH_29 = 2505600; |
||||
|
const NSInteger SECONDS_IN_MONTH_30 = 2592000; |
||||
|
const NSInteger SECONDS_IN_MONTH_31 = 2678400; |
||||
|
const NSInteger SECONDS_IN_WEEK = 604800; |
||||
|
const NSInteger SECONDS_IN_DAY = 86400; |
||||
|
const NSInteger SECONDS_IN_HOUR = 3600; |
||||
|
const NSInteger SECONDS_IN_MINUTE = 60; |
||||
|
const NSInteger MILLISECONDS_IN_DAY = 86400000; |
@ -0,0 +1,38 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
#pragma mark - Domain |
||||
|
extern NSString *const DTErrorDomain; |
||||
|
|
||||
|
#pragma mark - Status Codes |
||||
|
static const NSUInteger DTInsertOutOfBoundsException = 0; |
||||
|
static const NSUInteger DTRemoveOutOfBoundsException = 1; |
||||
|
static const NSUInteger DTBadTypeException = 2; |
||||
|
|
||||
|
@interface DTError : NSObject |
||||
|
|
||||
|
+(void)throwInsertOutOfBoundsException:(NSInteger)index array:(NSArray *)array; |
||||
|
+(void)throwRemoveOutOfBoundsException:(NSInteger)index array:(NSArray *)array; |
||||
|
+(void)throwBadTypeException:(id)obj expectedClass:(Class)classType; |
||||
|
@end |
@ -0,0 +1,72 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York |
||||
|
// |
||||
|
// Permission is hereby granted, free of charge, to any |
||||
|
// person obtaining a copy of this software and |
||||
|
// associated documentation files (the "Software"), to |
||||
|
// deal in the Software without restriction, including |
||||
|
// without limitation the rights to use, copy, modify, merge, |
||||
|
// publish, distribute, sublicense, and/or sell copies of the |
||||
|
// Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
// |
||||
|
// The above copyright notice and this permission notice shall |
||||
|
// be included in all copies or substantial portions of the Software. |
||||
|
// |
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
|
||||
|
#import "DTError.h" |
||||
|
|
||||
|
#pragma mark - Domain |
||||
|
NSString *const DTErrorDomain = @"com.mattyork.dateTools"; |
||||
|
|
||||
|
@implementation DTError |
||||
|
|
||||
|
+(void)throwInsertOutOfBoundsException:(NSInteger)index array:(NSArray *)array{ |
||||
|
//Handle possible zero bounds |
||||
|
NSInteger arrayUpperBound = (array.count == 0)? 0:array.count; |
||||
|
|
||||
|
//Create info for error |
||||
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to insert DTTimePeriod at index %ld but the group is of size [0...%ld].", (long)index, (long)arrayUpperBound],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try an index within the bounds or the group.", nil)}; |
||||
|
|
||||
|
//Handle Error |
||||
|
NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTInsertOutOfBoundsException userInfo:userInfo]; |
||||
|
[self printErrorWithCallStack:error]; |
||||
|
} |
||||
|
|
||||
|
+(void)throwRemoveOutOfBoundsException:(NSInteger)index array:(NSArray *)array{ |
||||
|
//Handle possible zero bounds |
||||
|
NSInteger arrayUpperBound = (array.count == 0)? 0:array.count; |
||||
|
|
||||
|
//Create info for error |
||||
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to remove DTTimePeriod at index %ld but the group is of size [0...%ld].", (long)index, (long)arrayUpperBound],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try an index within the bounds of the group.", nil)}; |
||||
|
|
||||
|
//Handle Error |
||||
|
NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTRemoveOutOfBoundsException userInfo:userInfo]; |
||||
|
[self printErrorWithCallStack:error]; |
||||
|
} |
||||
|
|
||||
|
+(void)throwBadTypeException:(id)obj expectedClass:(Class)classType{ |
||||
|
//Create info for error |
||||
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to insert object of class %@ when expecting object of class %@.", NSStringFromClass([obj class]), NSStringFromClass(classType)],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try again by inserting a DTTimePeriod object.", nil)}; |
||||
|
|
||||
|
//Handle Error |
||||
|
NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTBadTypeException userInfo:userInfo]; |
||||
|
[self printErrorWithCallStack:error]; |
||||
|
} |
||||
|
|
||||
|
+(void)printErrorWithCallStack:(NSError *)error{ |
||||
|
//Print error |
||||
|
NSLog(@"%@", error); |
||||
|
|
||||
|
//Print call stack |
||||
|
for (NSString *symbol in [NSThread callStackSymbols]) { |
||||
|
NSLog(@"\n\n %@", symbol); |
||||
|
} |
||||
|
} |
||||
|
@end |
@ -0,0 +1,123 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
|
||||
|
typedef NS_ENUM(NSUInteger, DTTimePeriodRelation){ |
||||
|
DTTimePeriodRelationAfter, |
||||
|
DTTimePeriodRelationStartTouching, |
||||
|
DTTimePeriodRelationStartInside, |
||||
|
DTTimePeriodRelationInsideStartTouching, |
||||
|
DTTimePeriodRelationEnclosingStartTouching, |
||||
|
DTTimePeriodRelationEnclosing, |
||||
|
DTTimePeriodRelationEnclosingEndTouching, |
||||
|
DTTimePeriodRelationExactMatch, |
||||
|
DTTimePeriodRelationInside, |
||||
|
DTTimePeriodRelationInsideEndTouching, |
||||
|
DTTimePeriodRelationEndInside, |
||||
|
DTTimePeriodRelationEndTouching, |
||||
|
DTTimePeriodRelationBefore, |
||||
|
DTTimePeriodRelationNone //One or more of the dates does not exist
|
||||
|
}; |
||||
|
|
||||
|
typedef NS_ENUM(NSUInteger, DTTimePeriodSize) { |
||||
|
DTTimePeriodSizeSecond, |
||||
|
DTTimePeriodSizeMinute, |
||||
|
DTTimePeriodSizeHour, |
||||
|
DTTimePeriodSizeDay, |
||||
|
DTTimePeriodSizeWeek, |
||||
|
DTTimePeriodSizeMonth, |
||||
|
DTTimePeriodSizeYear |
||||
|
}; |
||||
|
|
||||
|
typedef NS_ENUM(NSUInteger, DTTimePeriodInterval) { |
||||
|
DTTimePeriodIntervalOpen, |
||||
|
DTTimePeriodIntervalClosed |
||||
|
}; |
||||
|
|
||||
|
typedef NS_ENUM(NSUInteger, DTTimePeriodAnchor) { |
||||
|
DTTimePeriodAnchorStart, |
||||
|
DTTimePeriodAnchorCenter, |
||||
|
DTTimePeriodAnchorEnd |
||||
|
}; |
||||
|
|
||||
|
@interface DTTimePeriod : NSObject |
||||
|
|
||||
|
/**
|
||||
|
* The start date for a DTTimePeriod representing the starting boundary of the time period |
||||
|
*/ |
||||
|
@property (nonatomic,strong) NSDate *StartDate; |
||||
|
|
||||
|
/**
|
||||
|
* The end date for a DTTimePeriod representing the ending boundary of the time period |
||||
|
*/ |
||||
|
@property (nonatomic,strong) NSDate *EndDate; |
||||
|
|
||||
|
#pragma mark - Custom Init / Factory Methods |
||||
|
-(instancetype)initWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate; |
||||
|
+(instancetype)timePeriodWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate; |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size startingAt:(NSDate *)date; |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount startingAt:(NSDate *)date; |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size endingAt:(NSDate *)date; |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount endingAt:(NSDate *)date; |
||||
|
+(instancetype)timePeriodWithAllTime; |
||||
|
|
||||
|
#pragma mark - Time Period Information |
||||
|
-(BOOL)hasStartDate; |
||||
|
-(BOOL)hasEndDate; |
||||
|
-(BOOL)isMoment; |
||||
|
-(double)durationInYears; |
||||
|
-(double)durationInWeeks; |
||||
|
-(double)durationInDays; |
||||
|
-(double)durationInHours; |
||||
|
-(double)durationInMinutes; |
||||
|
-(double)durationInSeconds; |
||||
|
|
||||
|
#pragma mark - Time Period Relationship |
||||
|
-(BOOL)isEqualToPeriod:(DTTimePeriod *)period; |
||||
|
-(BOOL)isInside:(DTTimePeriod *)period; |
||||
|
-(BOOL)contains:(DTTimePeriod *)period; |
||||
|
-(BOOL)overlapsWith:(DTTimePeriod *)period; |
||||
|
-(BOOL)intersects:(DTTimePeriod *)period; |
||||
|
-(DTTimePeriodRelation)relationToPeriod:(DTTimePeriod *)period; |
||||
|
-(NSTimeInterval)gapBetween:(DTTimePeriod *)period; |
||||
|
|
||||
|
#pragma mark - Date Relationships |
||||
|
-(BOOL)containsDate:(NSDate *)date interval:(DTTimePeriodInterval)interval; |
||||
|
|
||||
|
#pragma mark - Period Manipulation |
||||
|
#pragma mark Shifts |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size; |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size; |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; |
||||
|
|
||||
|
#pragma mark Lengthen / Shorten |
||||
|
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size; |
||||
|
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount; |
||||
|
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size; |
||||
|
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount; |
||||
|
|
||||
|
#pragma mark - Helper Methods |
||||
|
-(DTTimePeriod *)copy; |
||||
|
@end |
@ -0,0 +1,642 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York |
||||
|
// |
||||
|
// Permission is hereby granted, free of charge, to any |
||||
|
// person obtaining a copy of this software and |
||||
|
// associated documentation files (the "Software"), to |
||||
|
// deal in the Software without restriction, including |
||||
|
// without limitation the rights to use, copy, modify, merge, |
||||
|
// publish, distribute, sublicense, and/or sell copies of the |
||||
|
// Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
// |
||||
|
// The above copyright notice and this permission notice shall |
||||
|
// be included in all copies or substantial portions of the Software. |
||||
|
// |
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
|
||||
|
#import "DTTimePeriod.h" |
||||
|
#import "NSDate+DateTools.h" |
||||
|
|
||||
|
@interface DTTimePeriod () |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
|
||||
|
@implementation DTTimePeriod |
||||
|
|
||||
|
#pragma mark - Custom Init / Factory Methods |
||||
|
/** |
||||
|
* Initializes an instance of DTTimePeriod from a given start and end date |
||||
|
* |
||||
|
* @param startDate NSDate - Desired start date |
||||
|
* @param endDate NSDate - Desired end date |
||||
|
* |
||||
|
* @return DTTimePeriod - new instance |
||||
|
*/ |
||||
|
-(instancetype)initWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate{ |
||||
|
if (self = [super init]) { |
||||
|
self.StartDate = startDate; |
||||
|
self.EndDate = endDate; |
||||
|
} |
||||
|
|
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a new instance of DTTimePeriod from a given start and end date |
||||
|
* |
||||
|
* @param startDate NSDate - Desired start date |
||||
|
* @param endDate NSDate - Desired end date |
||||
|
* |
||||
|
* @return DTTimePeriod - new instance |
||||
|
*/ |
||||
|
+(instancetype)timePeriodWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate{ |
||||
|
return [[self.class alloc] initWithStartDate:startDate endDate:endDate]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a new instance of DTTimePeriod that starts on the provided start date |
||||
|
* and is of the size provided |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired size of the new time period |
||||
|
* @param date NSDate - Desired start date of the new time period |
||||
|
* |
||||
|
* @return DTTimePeriod - new instance |
||||
|
*/ |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size startingAt:(NSDate *)date{ |
||||
|
return [[self.class alloc] initWithStartDate:date endDate:[DTTimePeriod dateWithAddedTime:size amount:1 baseDate:date]]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a new instance of DTTimePeriod that starts on the provided start date |
||||
|
* and is of the size provided. The amount represents a multipler to the size (e.g. "2 weeks" or "4 years") |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired size of the new time period |
||||
|
* @param amount NSInteger - Desired multiplier of the size provided |
||||
|
* @param date NSDate - Desired start date of the new time period |
||||
|
* |
||||
|
* @return DTTimePeriod - new instance |
||||
|
*/ |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount startingAt:(NSDate *)date{ |
||||
|
return [[self.class alloc] initWithStartDate:date endDate:[DTTimePeriod dateWithAddedTime:size amount:amount baseDate:date]]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a new instance of DTTimePeriod that ends on the provided end date |
||||
|
* and is of the size provided |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired size of the new time period |
||||
|
* @param date NSDate - Desired end date of the new time period |
||||
|
* |
||||
|
* @return DTTimePeriod - new instance |
||||
|
*/ |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size endingAt:(NSDate *)date{ |
||||
|
return [[self.class alloc] initWithStartDate:[DTTimePeriod dateWithSubtractedTime:size amount:1 baseDate:date] endDate:date]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a new instance of DTTimePeriod that ends on the provided end date |
||||
|
* and is of the size provided. The amount represents a multipler to the size (e.g. "2 weeks" or "4 years") |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired size of the new time period |
||||
|
* @param amount NSInteger - Desired multiplier of the size provided |
||||
|
* @param date NSDate - Desired end date of the new time period |
||||
|
* |
||||
|
* @return DTTimePeriod - new instance |
||||
|
*/ |
||||
|
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount endingAt:(NSDate *)date{ |
||||
|
return [[self.class alloc] initWithStartDate:[DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:date] endDate:date]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a new instance of DTTimePeriod that represents the largest time period available. |
||||
|
* The start date is in the distant past and the end date is in the distant future. |
||||
|
* |
||||
|
* @return DTTimePeriod - new instance |
||||
|
*/ |
||||
|
+(instancetype)timePeriodWithAllTime{ |
||||
|
return [[self.class alloc] initWithStartDate:[NSDate distantPast] endDate:[NSDate distantFuture]]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Method serving the various factory methods as well as a few others. |
||||
|
* Returns a date with time added to a given base date. Includes multiplier amount. |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired size of the new time period |
||||
|
* @param amount NSInteger - Desired multiplier of the size provided |
||||
|
* @param date NSDate - Desired end date of the new time period |
||||
|
* |
||||
|
* @return NSDate - new instance |
||||
|
*/ |
||||
|
+(NSDate *)dateWithAddedTime:(DTTimePeriodSize)size amount:(NSInteger)amount baseDate:(NSDate *)date{ |
||||
|
switch (size) { |
||||
|
case DTTimePeriodSizeSecond: |
||||
|
return [date dateByAddingSeconds:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeMinute: |
||||
|
return [date dateByAddingMinutes:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeHour: |
||||
|
return [date dateByAddingHours:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeDay: |
||||
|
return [date dateByAddingDays:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeWeek: |
||||
|
return [date dateByAddingWeeks:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeMonth: |
||||
|
return [date dateByAddingMonths:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeYear: |
||||
|
return [date dateByAddingYears:amount]; |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Method serving the various factory methods as well as a few others. |
||||
|
* Returns a date with time subtracted from a given base date. Includes multiplier amount. |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired size of the new time period |
||||
|
* @param amount NSInteger - Desired multiplier of the size provided |
||||
|
* @param date NSDate - Desired end date of the new time period |
||||
|
* |
||||
|
* @return NSDate - new instance |
||||
|
*/ |
||||
|
+(NSDate *)dateWithSubtractedTime:(DTTimePeriodSize)size amount:(NSInteger)amount baseDate:(NSDate *)date{ |
||||
|
switch (size) { |
||||
|
case DTTimePeriodSizeSecond: |
||||
|
return [date dateBySubtractingSeconds:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeMinute: |
||||
|
return [date dateBySubtractingMinutes:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeHour: |
||||
|
return [date dateBySubtractingHours:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeDay: |
||||
|
return [date dateBySubtractingDays:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeWeek: |
||||
|
return [date dateBySubtractingWeeks:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeMonth: |
||||
|
return [date dateBySubtractingMonths:amount]; |
||||
|
break; |
||||
|
case DTTimePeriodSizeYear: |
||||
|
return [date dateBySubtractingYears:amount]; |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Time Period Information |
||||
|
/** |
||||
|
* Returns a boolean representing whether the receiver's StartDate exists |
||||
|
* Returns YES if StartDate is not nil, otherwise NO |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)hasStartDate { |
||||
|
return (self.StartDate)? YES:NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a boolean representing whether the receiver's EndDate exists |
||||
|
* Returns YES if EndDate is not nil, otherwise NO |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)hasEndDate { |
||||
|
return (self.EndDate)? YES:NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a boolean representing whether the receiver is a "moment", that is the start and end dates are the same. |
||||
|
* Returns YES if receiver is a moment, otherwise NO |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)isMoment{ |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
if ([self.StartDate isEqualToDate:self.EndDate]) { |
||||
|
return YES; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in years |
||||
|
* |
||||
|
* @return NSInteger |
||||
|
*/ |
||||
|
-(double)durationInYears { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate yearsEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in weeks |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInWeeks { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate weeksEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in days |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInDays { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate daysEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in hours |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInHours { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate hoursEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in minutes |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInMinutes { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate minutesEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in seconds |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInSeconds { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate secondsEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Time Period Relationship |
||||
|
/** |
||||
|
* Returns a BOOL representing whether the receiver's start and end dates exatcly match a given time period |
||||
|
* Returns YES if the two periods are the same, otherwise NO |
||||
|
* |
||||
|
* @param period DTTimePeriod - Time period to compare to receiver |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)isEqualToPeriod:(DTTimePeriod *)period{ |
||||
|
if ([self.StartDate isEqualToDate:period.StartDate] && [self.EndDate isEqualToDate:period.EndDate]) { |
||||
|
return YES; |
||||
|
} |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a BOOL representing whether the receiver's start and end dates exatcly match a given time period or is contained within them |
||||
|
* Returns YES if the receiver is inside the given time period, otherwise NO |
||||
|
* |
||||
|
* @param period DTTimePeriod - Time period to compare to receiver |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)isInside:(DTTimePeriod *)period{ |
||||
|
if ([period.StartDate isEarlierThanOrEqualTo:self.StartDate] && [period.EndDate isLaterThanOrEqualTo:self.EndDate]) { |
||||
|
return YES; |
||||
|
} |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a BOOL representing whether the given time period's start and end dates exatcly match the receivers' or is contained within them |
||||
|
* Returns YES if the receiver is inside the given time period, otherwise NO |
||||
|
* |
||||
|
* @param period DTTimePeriod - Time period to compare to receiver |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)contains:(DTTimePeriod *)period{ |
||||
|
if ([self.StartDate isEarlierThanOrEqualTo:period.StartDate] && [self.EndDate isLaterThanOrEqualTo:period.EndDate]) { |
||||
|
return YES; |
||||
|
} |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a BOOL representing whether the receiver and the given time period overlap. |
||||
|
* This covers all space they share, minus instantaneous space (i.e. one's start date equals another's end date) |
||||
|
* Returns YES if they overlap, otherwise NO |
||||
|
* |
||||
|
* @param period DTTimePeriod - Time period to compare to receiver |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)overlapsWith:(DTTimePeriod *)period{ |
||||
|
//Outside -> Inside |
||||
|
if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThan:self.StartDate]) { |
||||
|
return YES; |
||||
|
} |
||||
|
//Enclosing |
||||
|
else if ([period.StartDate isLaterThanOrEqualTo:self.StartDate] && [period.EndDate isEarlierThanOrEqualTo:self.EndDate]){ |
||||
|
return YES; |
||||
|
} |
||||
|
//Inside -> Out |
||||
|
else if([period.StartDate isEarlierThan:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ |
||||
|
return YES; |
||||
|
} |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a BOOL representing whether the receiver and the given time period overlap. |
||||
|
* This covers all space they share, including instantaneous space (i.e. one's start date equals another's end date) |
||||
|
* Returns YES if they overlap, otherwise NO |
||||
|
* |
||||
|
* @param period DTTimePeriod - Time period to compare to receiver |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)intersects:(DTTimePeriod *)period{ |
||||
|
//Outside -> Inside |
||||
|
if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThanOrEqualTo:self.StartDate]) { |
||||
|
return YES; |
||||
|
} |
||||
|
//Enclosing |
||||
|
else if ([period.StartDate isLaterThanOrEqualTo:self.StartDate] && [period.EndDate isEarlierThanOrEqualTo:self.EndDate]){ |
||||
|
return YES; |
||||
|
} |
||||
|
//Inside -> Out |
||||
|
else if([period.StartDate isEarlierThanOrEqualTo:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ |
||||
|
return YES; |
||||
|
} |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the relationship of the receiver to a given time period |
||||
|
* |
||||
|
* @param period DTTimePeriod - Time period to compare to receiver |
||||
|
* |
||||
|
* @return DTTimePeriodRelation |
||||
|
*/ |
||||
|
-(DTTimePeriodRelation)relationToPeriod:(DTTimePeriod *)period{ |
||||
|
|
||||
|
//Make sure that all start and end points exist for comparison |
||||
|
if (self.StartDate && self.EndDate && period.StartDate && period.EndDate) { |
||||
|
//Make sure time periods are of positive durations |
||||
|
if ([self.StartDate isEarlierThan:self.EndDate] && [period.StartDate isEarlierThan:period.EndDate]) { |
||||
|
|
||||
|
//Make comparisons |
||||
|
if ([period.EndDate isEarlierThan:self.StartDate]) { |
||||
|
return DTTimePeriodRelationAfter; |
||||
|
} |
||||
|
else if ([period.EndDate isEqualToDate:self.StartDate]){ |
||||
|
return DTTimePeriodRelationStartTouching; |
||||
|
} |
||||
|
else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationStartInside; |
||||
|
} |
||||
|
else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isLaterThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationInsideStartTouching; |
||||
|
} |
||||
|
else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationEnclosingStartTouching; |
||||
|
} |
||||
|
else if ([period.StartDate isLaterThan:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationEnclosing; |
||||
|
} |
||||
|
else if ([period.StartDate isLaterThan:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){ |
||||
|
return DTTimePeriodRelationEnclosingEndTouching; |
||||
|
} |
||||
|
else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){ |
||||
|
return DTTimePeriodRelationExactMatch; |
||||
|
} |
||||
|
else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationInside; |
||||
|
} |
||||
|
else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){ |
||||
|
return DTTimePeriodRelationInsideEndTouching; |
||||
|
} |
||||
|
else if ([period.StartDate isEarlierThan:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationEndInside; |
||||
|
} |
||||
|
else if ([period.StartDate isEqualToDate:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationEndTouching; |
||||
|
} |
||||
|
else if ([period.StartDate isLaterThan:self.EndDate]){ |
||||
|
return DTTimePeriodRelationBefore; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return DTTimePeriodRelationNone; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the gap in seconds between the receiver and provided time period |
||||
|
* Returns 0 if the time periods intersect, otherwise returns the gap between. |
||||
|
* |
||||
|
* @param period <#period description#> |
||||
|
* |
||||
|
* @return <#return value description#> |
||||
|
*/ |
||||
|
-(NSTimeInterval)gapBetween:(DTTimePeriod *)period{ |
||||
|
if ([self.EndDate isEarlierThan:period.StartDate]) { |
||||
|
return ABS([self.EndDate timeIntervalSinceDate:period.StartDate]); |
||||
|
} |
||||
|
else if ([period.EndDate isEarlierThan:self.StartDate]){ |
||||
|
return ABS([period.EndDate timeIntervalSinceDate:self.StartDate]); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Date Relationships |
||||
|
/** |
||||
|
* Returns a BOOL representing whether the provided date is contained in the receiver. |
||||
|
* |
||||
|
* @param date NSDate - Date to evaluate |
||||
|
* @param interval DTTimePeriodInterval representing evaluation type (Closed includes StartDate and EndDate in evaluation, Open does not) |
||||
|
* |
||||
|
* @return <#return value description#> |
||||
|
*/ |
||||
|
-(BOOL)containsDate:(NSDate *)date interval:(DTTimePeriodInterval)interval{ |
||||
|
if (interval == DTTimePeriodIntervalOpen) { |
||||
|
if ([self.StartDate isEarlierThan:date] && [self.EndDate isLaterThan:date]) { |
||||
|
return YES; |
||||
|
} |
||||
|
else { |
||||
|
return NO; |
||||
|
} |
||||
|
} |
||||
|
else if (interval == DTTimePeriodIntervalClosed){ |
||||
|
if ([self.StartDate isEarlierThanOrEqualTo:date] && [self.EndDate isLaterThanOrEqualTo:date]) { |
||||
|
return YES; |
||||
|
} |
||||
|
else { |
||||
|
return NO; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Period Manipulation |
||||
|
/** |
||||
|
* Shifts the StartDate and EndDate earlier by a given size amount |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired shift size |
||||
|
*/ |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size{ |
||||
|
[self shiftEarlierWithSize:size amount:1]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shifts the StartDate and EndDate earlier by a given size amount. Amount multiplies size. |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired shift size |
||||
|
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") |
||||
|
*/ |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ |
||||
|
self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.StartDate]; |
||||
|
self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shifts the StartDate and EndDate later by a given size amount |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired shift size |
||||
|
*/ |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size{ |
||||
|
[self shiftLaterWithSize:size amount:1]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shifts the StartDate and EndDate later by a given size amount. Amount multiplies size. |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - Desired shift size |
||||
|
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") |
||||
|
*/ |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ |
||||
|
self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.StartDate]; |
||||
|
self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark Lengthen / Shorten |
||||
|
/** |
||||
|
* Lengthens the receiver by a given amount, anchored by a provided point |
||||
|
* |
||||
|
* @param anchor DTTimePeriodAnchor - Anchor point for the lengthen (the date that stays the same) |
||||
|
* @param size DTTimePeriodSize - Desired lenghtening size |
||||
|
*/ |
||||
|
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size{ |
||||
|
[self lengthenWithAnchorDate:anchor size:size amount:1]; |
||||
|
} |
||||
|
/** |
||||
|
* Lengthens the receiver by a given amount, anchored by a provided point. Amount multiplies size. |
||||
|
* |
||||
|
* @param anchor DTTimePeriodAnchor - Anchor point for the lengthen (the date that stays the same) |
||||
|
* @param size DTTimePeriodSize - Desired lenghtening size |
||||
|
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") |
||||
|
*/ |
||||
|
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount{ |
||||
|
switch (anchor) { |
||||
|
case DTTimePeriodAnchorStart: |
||||
|
self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.EndDate]; |
||||
|
break; |
||||
|
case DTTimePeriodAnchorCenter: |
||||
|
self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount/2 baseDate:self.StartDate]; |
||||
|
self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount/2 baseDate:self.EndDate]; |
||||
|
break; |
||||
|
case DTTimePeriodAnchorEnd: |
||||
|
self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.StartDate]; |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shortens the receiver by a given amount, anchored by a provided point |
||||
|
* |
||||
|
* @param anchor DTTimePeriodAnchor - Anchor point for the shorten (the date that stays the same) |
||||
|
* @param size DTTimePeriodSize - Desired shortening size |
||||
|
*/ |
||||
|
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size{ |
||||
|
[self shortenWithAnchorDate:anchor size:size amount:1]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shortens the receiver by a given amount, anchored by a provided point. Amount multiplies size. |
||||
|
* |
||||
|
* @param anchor DTTimePeriodAnchor - Anchor point for the shorten (the date that stays the same) |
||||
|
* @param size DTTimePeriodSize - Desired shortening size |
||||
|
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") |
||||
|
*/ |
||||
|
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount{ |
||||
|
switch (anchor) { |
||||
|
case DTTimePeriodAnchorStart: |
||||
|
self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.EndDate]; |
||||
|
break; |
||||
|
case DTTimePeriodAnchorCenter: |
||||
|
self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount/2 baseDate:self.StartDate]; |
||||
|
self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount/2 baseDate:self.EndDate]; |
||||
|
break; |
||||
|
case DTTimePeriodAnchorEnd: |
||||
|
self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.StartDate]; |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Helper Methods |
||||
|
-(DTTimePeriod *)copy{ |
||||
|
DTTimePeriod *period = [DTTimePeriod timePeriodWithStartDate:[NSDate dateWithTimeIntervalSince1970:self.StartDate.timeIntervalSince1970] endDate:[NSDate dateWithTimeIntervalSince1970:self.EndDate.timeIntervalSince1970]]; |
||||
|
return period; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,49 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "DTTimePeriodGroup.h" |
||||
|
|
||||
|
@interface DTTimePeriodChain : DTTimePeriodGroup { |
||||
|
DTTimePeriod *First; |
||||
|
DTTimePeriod *Last; |
||||
|
} |
||||
|
|
||||
|
@property (nonatomic, readonly) DTTimePeriod *First; |
||||
|
@property (nonatomic, readonly) DTTimePeriod *Last; |
||||
|
|
||||
|
#pragma mark - Custom Init / Factory Chain |
||||
|
+(DTTimePeriodChain *)chain; |
||||
|
|
||||
|
#pragma mark - Chain Existence Manipulation |
||||
|
-(void)addTimePeriod:(DTTimePeriod *)period; |
||||
|
-(void)insertTimePeriod:(DTTimePeriod *)period atInedx:(NSInteger)index; |
||||
|
-(void)removeTimePeriodAtIndex:(NSInteger)index; |
||||
|
-(void)removeLatestTimePeriod; |
||||
|
-(void)removeEarliestTimePeriod; |
||||
|
|
||||
|
#pragma mark - Chain Relationship |
||||
|
-(BOOL)isEqualToChain:(DTTimePeriodChain *)chain; |
||||
|
|
||||
|
#pragma mark - Updates |
||||
|
-(void)updateVariables; |
||||
|
@end |
@ -0,0 +1,218 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York |
||||
|
// |
||||
|
// Permission is hereby granted, free of charge, to any |
||||
|
// person obtaining a copy of this software and |
||||
|
// associated documentation files (the "Software"), to |
||||
|
// deal in the Software without restriction, including |
||||
|
// without limitation the rights to use, copy, modify, merge, |
||||
|
// publish, distribute, sublicense, and/or sell copies of the |
||||
|
// Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
// |
||||
|
// The above copyright notice and this permission notice shall |
||||
|
// be included in all copies or substantial portions of the Software. |
||||
|
// |
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
|
||||
|
#import "DTTimePeriodChain.h" |
||||
|
#import "DTError.h" |
||||
|
|
||||
|
@interface DTTimePeriodChain () |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@implementation DTTimePeriodChain |
||||
|
|
||||
|
#pragma mark - Custom Init / Factory Chain |
||||
|
+(DTTimePeriodChain *)chain{ |
||||
|
return [[DTTimePeriodChain alloc] init]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Chain Existence Manipulation |
||||
|
-(void)addTimePeriod:(DTTimePeriod *)period{ |
||||
|
if ([period class] != [DTTimePeriod class]) { |
||||
|
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (periods) { |
||||
|
if (periods.count > 0) { |
||||
|
//Create a modified period to be added based on size of passed in period |
||||
|
DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds startingAt:[periods[periods.count - 1] EndDate]]; |
||||
|
|
||||
|
//Add object to periods array |
||||
|
[periods addObject:modifiedPeriod]; |
||||
|
} |
||||
|
else { |
||||
|
//Add object to periods array |
||||
|
[periods addObject:period]; |
||||
|
} |
||||
|
} |
||||
|
else { |
||||
|
//Create new periods array |
||||
|
periods = [NSMutableArray array]; |
||||
|
|
||||
|
//Add object to periods array |
||||
|
[periods addObject:period]; |
||||
|
} |
||||
|
|
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
|
||||
|
-(void)insertTimePeriod:(DTTimePeriod *)period atInedx:(NSInteger)index{ |
||||
|
if ([period class] != [DTTimePeriod class]) { |
||||
|
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
//Make sure the index is within the operable bounds of the periods array |
||||
|
if (index == 0) { |
||||
|
//Update bounds of period to make it fit in chain |
||||
|
DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds endingAt:[periods[0] EndDate]]; |
||||
|
|
||||
|
//Insert the updated object at the beginning of the periods array |
||||
|
[periods insertObject:modifiedPeriod atIndex:0]; |
||||
|
} |
||||
|
else if (index > 0 && index < periods.count) { |
||||
|
|
||||
|
//Shift time periods later if they fall after new period |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
//Shift later |
||||
|
if (idx >= index) { |
||||
|
[((DTTimePeriod *) obj) shiftLaterWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds]; |
||||
|
} |
||||
|
}]; |
||||
|
|
||||
|
//Update bounds of period to make it fit in chain |
||||
|
DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds startingAt:[periods[index - 1] EndDate]]; |
||||
|
|
||||
|
//Insert the updated object at the beginning of the periods array |
||||
|
[periods insertObject:modifiedPeriod atIndex:index]; |
||||
|
|
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwInsertOutOfBoundsException:index array:periods]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
-(void)removeTimePeriodAtIndex:(NSInteger)index{ |
||||
|
//Make sure the index is within the operable bounds of the periods array |
||||
|
if (index >= 0 && index < periods.count) { |
||||
|
DTTimePeriod *period = periods[index]; |
||||
|
|
||||
|
//Shift time periods later if they fall after new period |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
//Shift earlier |
||||
|
if (idx > index) { |
||||
|
[((DTTimePeriod *) obj) shiftEarlierWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds]; |
||||
|
} |
||||
|
}]; |
||||
|
|
||||
|
//Remove object |
||||
|
[periods removeObjectAtIndex:index]; |
||||
|
|
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwRemoveOutOfBoundsException:index array:periods]; |
||||
|
} |
||||
|
} |
||||
|
-(void)removeLatestTimePeriod{ |
||||
|
if (periods.count > 0) { |
||||
|
[periods removeLastObject]; |
||||
|
|
||||
|
//Update the object variables |
||||
|
if (periods.count > 0) { |
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
else { |
||||
|
[self setVariablesNil]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
-(void)removeEarliestTimePeriod{ |
||||
|
if (periods > 0) { |
||||
|
//Shift time periods earlier |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
//Shift earlier to account for removal of first element in periods array |
||||
|
[((DTTimePeriod *) obj) shiftEarlierWithSize:DTTimePeriodSizeSecond amount:[periods[0] durationInSeconds]]; |
||||
|
}]; |
||||
|
|
||||
|
//Remove first period |
||||
|
[periods removeObjectAtIndex:0]; |
||||
|
|
||||
|
//Update the object variables |
||||
|
if (periods.count > 0) { |
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
else { |
||||
|
[self setVariablesNil]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Chain Relationship |
||||
|
-(BOOL)isEqualToChain:(DTTimePeriodChain *)chain{ |
||||
|
//Check class |
||||
|
if ([chain class] != [DTTimePeriodChain class]) { |
||||
|
[DTError throwBadTypeException:chain expectedClass:[DTTimePeriodChain class]]; |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
//Check group level characteristics for speed |
||||
|
if (![self hasSameCharacteristicsAs:chain]) { |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
//Check whole chain |
||||
|
__block BOOL isEqual = YES; |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
if (![chain[idx] isEqualToPeriod:obj]) { |
||||
|
isEqual = NO; |
||||
|
*stop = YES; |
||||
|
} |
||||
|
}]; |
||||
|
return isEqual; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Getters |
||||
|
|
||||
|
-(DTTimePeriod *)First{ |
||||
|
return First; |
||||
|
} |
||||
|
|
||||
|
-(DTTimePeriod *)Last{ |
||||
|
return Last; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Helper Methods |
||||
|
|
||||
|
-(void)updateVariables{ |
||||
|
//Set helper variables |
||||
|
StartDate = [periods[0] StartDate]; |
||||
|
EndDate = [periods[periods.count - 1] EndDate]; |
||||
|
First = periods[0]; |
||||
|
Last = periods[periods.count -1]; |
||||
|
} |
||||
|
|
||||
|
-(void)setVariablesNil{ |
||||
|
//Set helper variables |
||||
|
StartDate = nil; |
||||
|
EndDate = nil; |
||||
|
First = nil; |
||||
|
Last = nil; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,56 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "DTTimePeriodGroup.h" |
||||
|
|
||||
|
@interface DTTimePeriodCollection : DTTimePeriodGroup |
||||
|
|
||||
|
#pragma mark - Custom Init / Factory Methods |
||||
|
+(DTTimePeriodCollection *)collection; |
||||
|
|
||||
|
#pragma mark - Collection Manipulation |
||||
|
-(void)addTimePeriod:(DTTimePeriod *)period; |
||||
|
-(void)insertTimePeriod:(DTTimePeriod *)period atIndex:(NSInteger)index; |
||||
|
-(void)removeTimePeriodAtIndex:(NSInteger)index; |
||||
|
|
||||
|
#pragma mark - Sorting |
||||
|
-(void)sortByStartAscending; |
||||
|
-(void)sortByStartDescending; |
||||
|
-(void)sortByEndAscending; |
||||
|
-(void)sortByEndDescending; |
||||
|
-(void)sortByDurationAscending; |
||||
|
-(void)sortByDurationDescending; |
||||
|
|
||||
|
#pragma mark - Collection Relationship |
||||
|
-(DTTimePeriodCollection *)periodsInside:(DTTimePeriod *)period; |
||||
|
-(DTTimePeriodCollection *)periodsIntersectedByDate:(NSDate *)date; |
||||
|
-(DTTimePeriodCollection *)periodsIntersectedByPeriod:(DTTimePeriod *)period; |
||||
|
-(DTTimePeriodCollection *)periodsOverlappedByPeriod:(DTTimePeriod *)period; |
||||
|
-(BOOL)isEqualToCollection:(DTTimePeriodCollection *)collection considerOrder:(BOOL)considerOrder; |
||||
|
|
||||
|
#pragma mark - Helper Methods |
||||
|
-(DTTimePeriodCollection *)copy; |
||||
|
|
||||
|
#pragma mark - Updates |
||||
|
-(void)updateVariables; |
||||
|
@end |
@ -0,0 +1,370 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York |
||||
|
// |
||||
|
// Permission is hereby granted, free of charge, to any |
||||
|
// person obtaining a copy of this software and |
||||
|
// associated documentation files (the "Software"), to |
||||
|
// deal in the Software without restriction, including |
||||
|
// without limitation the rights to use, copy, modify, merge, |
||||
|
// publish, distribute, sublicense, and/or sell copies of the |
||||
|
// Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
// |
||||
|
// The above copyright notice and this permission notice shall |
||||
|
// be included in all copies or substantial portions of the Software. |
||||
|
// |
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
|
||||
|
#import "DTTimePeriodCollection.h" |
||||
|
#import "DTError.h" |
||||
|
#import "NSDate+DateTools.h" |
||||
|
|
||||
|
@implementation DTTimePeriodCollection |
||||
|
|
||||
|
#pragma mark - Custom Init / Factory Methods |
||||
|
/** |
||||
|
* Initializes a new instance of DTTimePeriodCollection |
||||
|
* |
||||
|
* @return DTTimePeriodCollection |
||||
|
*/ |
||||
|
+(DTTimePeriodCollection *)collection{ |
||||
|
return [[DTTimePeriodCollection alloc] init]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Collection Manipulation |
||||
|
/** |
||||
|
* Adds a time period to the reciever. |
||||
|
* |
||||
|
* @param period DTTimePeriod - The time period to add to the collection |
||||
|
*/ |
||||
|
-(void)addTimePeriod:(DTTimePeriod *)period{ |
||||
|
if ([period isKindOfClass:[DTTimePeriod class]]) { |
||||
|
[periods addObject:period]; |
||||
|
|
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Inserts a time period to the receiver at a given index. |
||||
|
* |
||||
|
* @param period DTTimePeriod - The time period to insert into the collection |
||||
|
* @param index NSInteger - The index in the collection the time period is to be added at |
||||
|
*/ |
||||
|
-(void)insertTimePeriod:(DTTimePeriod *)period atIndex:(NSInteger)index{ |
||||
|
if ([period class] != [DTTimePeriod class]) { |
||||
|
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (index >= 0 && index < periods.count) { |
||||
|
[periods insertObject:period atIndex:index]; |
||||
|
|
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwInsertOutOfBoundsException:index array:periods]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Removes the time period at a given index from the collection |
||||
|
* |
||||
|
* @param index NSInteger - The index in the collection the time period is to be removed from |
||||
|
*/ |
||||
|
-(void)removeTimePeriodAtIndex:(NSInteger)index{ |
||||
|
if (index >= 0 && index < periods.count) { |
||||
|
[periods removeObjectAtIndex:index]; |
||||
|
|
||||
|
//Update the object variables |
||||
|
if (periods.count > 0) { |
||||
|
//Set object's variables with updated array values |
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
else { |
||||
|
[self setVariablesNil]; |
||||
|
} |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwRemoveOutOfBoundsException:index array:periods]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
#pragma mark - Sorting |
||||
|
/** |
||||
|
* Sorts the time periods in the collection by earliest start date to latest start date. |
||||
|
*/ |
||||
|
-(void)sortByStartAscending{ |
||||
|
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { |
||||
|
return [((DTTimePeriod *) obj1).StartDate compare:((DTTimePeriod *) obj2).StartDate]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sorts the time periods in the collection by latest start date to earliest start date. |
||||
|
*/ |
||||
|
-(void)sortByStartDescending{ |
||||
|
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { |
||||
|
return [((DTTimePeriod *) obj2).StartDate compare:((DTTimePeriod *) obj1).StartDate]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sorts the time periods in the collection by earliest end date to latest end date. |
||||
|
*/ |
||||
|
-(void)sortByEndAscending{ |
||||
|
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { |
||||
|
return [((DTTimePeriod *) obj1).EndDate compare:((DTTimePeriod *) obj2).EndDate]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sorts the time periods in the collection by latest end date to earliest end date. |
||||
|
*/ |
||||
|
-(void)sortByEndDescending{ |
||||
|
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { |
||||
|
return [((DTTimePeriod *) obj2).EndDate compare:((DTTimePeriod *) obj1).EndDate]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sorts the time periods in the collection by how much time they span. Sorts smallest durations to longest. |
||||
|
*/ |
||||
|
-(void)sortByDurationAscending{ |
||||
|
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { |
||||
|
if (((DTTimePeriod *) obj1).durationInSeconds < ((DTTimePeriod *) obj2).durationInSeconds) { |
||||
|
return NSOrderedAscending; |
||||
|
} |
||||
|
else { |
||||
|
return NSOrderedDescending; |
||||
|
} |
||||
|
return NSOrderedSame; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sorts the time periods in the collection by how much time they span. Sorts longest durations to smallest. |
||||
|
*/ |
||||
|
-(void)sortByDurationDescending{ |
||||
|
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { |
||||
|
if (((DTTimePeriod *) obj1).durationInSeconds > ((DTTimePeriod *) obj2).durationInSeconds) { |
||||
|
return NSOrderedAscending; |
||||
|
} |
||||
|
else { |
||||
|
return NSOrderedDescending; |
||||
|
} |
||||
|
return NSOrderedSame; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Collection Relationship |
||||
|
/** |
||||
|
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that fall inside a given time period. |
||||
|
* Time periods of the receiver must have a start date and end date within the closed interval of the period provided to be included. |
||||
|
* |
||||
|
* @param period DTTimePeriod - The time period to check against the receiver's time periods. |
||||
|
* |
||||
|
* @return DTTimePeriodCollection |
||||
|
*/ |
||||
|
-(DTTimePeriodCollection *)periodsInside:(DTTimePeriod *)period{ |
||||
|
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; |
||||
|
|
||||
|
if ([period isKindOfClass:[DTTimePeriod class]]) { |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
if ([((DTTimePeriod *) obj) isInside:period]) { |
||||
|
[collection addTimePeriod:obj]; |
||||
|
} |
||||
|
}]; |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; |
||||
|
} |
||||
|
|
||||
|
return collection; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given date. |
||||
|
* Time periods of the receiver must have a start date earlier than or equal to the comparison date and an end date later than or equal to the comparison date to be included |
||||
|
* |
||||
|
* @param date NSDate - The date to check against the receiver's time periods |
||||
|
* |
||||
|
* @return DTTimePeriodCollection |
||||
|
*/ |
||||
|
-(DTTimePeriodCollection *)periodsIntersectedByDate:(NSDate *)date{ |
||||
|
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; |
||||
|
|
||||
|
if ([date isKindOfClass:[NSDate class]]) { |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
if ([((DTTimePeriod *) obj) containsDate:date interval:DTTimePeriodIntervalClosed]) { |
||||
|
[collection addTimePeriod:obj]; |
||||
|
} |
||||
|
}]; |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwBadTypeException:date expectedClass:[NSDate class]]; |
||||
|
} |
||||
|
|
||||
|
return collection; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given time period. |
||||
|
* Intersection with the given time period includes other time periods that simply touch it. (i.e. one's start date is equal to another's end date) |
||||
|
* |
||||
|
* @param period DTTimePeriod - The time period to check against the receiver's time periods. |
||||
|
* |
||||
|
* @return DTTimePeriodCollection |
||||
|
*/ |
||||
|
-(DTTimePeriodCollection *)periodsIntersectedByPeriod:(DTTimePeriod *)period{ |
||||
|
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; |
||||
|
|
||||
|
if ([period isKindOfClass:[DTTimePeriod class]]) { |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
if ([((DTTimePeriod *) obj) intersects:period]) { |
||||
|
[collection addTimePeriod:obj]; |
||||
|
} |
||||
|
}]; |
||||
|
} |
||||
|
else { |
||||
|
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; |
||||
|
} |
||||
|
|
||||
|
return collection; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that overlap a given time period. |
||||
|
* Overlap with the given time period does NOT include other time periods that simply touch it. (i.e. one's start date is equal to another's end date) |
||||
|
* |
||||
|
* @param period DTTimePeriod - The time period to check against the receiver's time periods. |
||||
|
* |
||||
|
* @return DTTimePeriodCollection |
||||
|
*/ |
||||
|
-(DTTimePeriodCollection *)periodsOverlappedByPeriod:(DTTimePeriod *)period{ |
||||
|
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; |
||||
|
|
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
if ([((DTTimePeriod *) obj) overlapsWith:period]) { |
||||
|
[collection addTimePeriod:obj]; |
||||
|
} |
||||
|
}]; |
||||
|
|
||||
|
return collection; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a BOOL representing whether the receiver is equal to a given DTTimePeriodCollection. Equality requires the start and end dates to be the same, and all time periods to be the same. |
||||
|
* |
||||
|
* If you would like to take the order of the time periods in two collections into consideration, you may do so with the considerOrder BOOL |
||||
|
* |
||||
|
* @param collection DTTimePeriodCollection - The collection to compare with the receiver |
||||
|
* @param considerOrder BOOL - Option for whether to account for the time periods order in the test for equality. YES considers order, NO does not. |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)isEqualToCollection:(DTTimePeriodCollection *)collection considerOrder:(BOOL)considerOrder{ |
||||
|
//Check class |
||||
|
if ([collection class] != [DTTimePeriodCollection class]) { |
||||
|
[DTError throwBadTypeException:collection expectedClass:[DTTimePeriodCollection class]]; |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
//Check group level characteristics for speed |
||||
|
if (![self hasSameCharacteristicsAs:collection]) { |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
//Default to equality and look for inequality |
||||
|
__block BOOL isEqual = YES; |
||||
|
if (considerOrder) { |
||||
|
|
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
if (![collection[idx] isEqualToPeriod:obj]) { |
||||
|
isEqual = NO; |
||||
|
*stop = YES; |
||||
|
} |
||||
|
}]; |
||||
|
} |
||||
|
else { |
||||
|
__block DTTimePeriodCollection *collectionCopy = [collection copy]; |
||||
|
|
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
__block BOOL innerMatch = NO; |
||||
|
__block NSInteger matchIndex = 0; //We will remove matches to account for duplicates and to help speed |
||||
|
for (int ii = 0; ii < collectionCopy.count; ii++) { |
||||
|
if ([obj isEqualToPeriod:collectionCopy[ii]]) { |
||||
|
innerMatch = YES; |
||||
|
matchIndex = ii; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If there was a match found, stop |
||||
|
if (!innerMatch) { |
||||
|
isEqual = NO; |
||||
|
*stop = YES; |
||||
|
} |
||||
|
else { |
||||
|
[collectionCopy removeTimePeriodAtIndex:matchIndex]; |
||||
|
} |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
return isEqual; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Helper Methods |
||||
|
|
||||
|
-(void)updateVariables{ |
||||
|
//Set helper variables |
||||
|
__block NSDate *startDate = [NSDate distantFuture]; |
||||
|
__block NSDate *endDate = [NSDate distantPast]; |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
if ([((DTTimePeriod *) obj).StartDate isEarlierThan:startDate]) { |
||||
|
startDate = ((DTTimePeriod *) obj).StartDate; |
||||
|
} |
||||
|
if ([((DTTimePeriod *) obj).EndDate isLaterThan:endDate]) { |
||||
|
endDate = ((DTTimePeriod *) obj).EndDate; |
||||
|
} |
||||
|
}]; |
||||
|
|
||||
|
//Make assignments after evaluation |
||||
|
StartDate = startDate; |
||||
|
EndDate = endDate; |
||||
|
} |
||||
|
|
||||
|
-(void)setVariablesNil{ |
||||
|
//Set helper variables |
||||
|
StartDate = nil; |
||||
|
EndDate = nil; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a new instance of DTTimePeriodCollection that is an exact copy of the receiver, but with differnt memory references, etc. |
||||
|
* |
||||
|
* @return DTTimePeriodCollection |
||||
|
*/ |
||||
|
-(DTTimePeriodCollection *)copy{ |
||||
|
DTTimePeriodCollection *collection = [DTTimePeriodCollection collection]; |
||||
|
|
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
[collection addTimePeriod:[obj copy]]; |
||||
|
}]; |
||||
|
|
||||
|
return collection; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,62 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "DTTimePeriod.h" |
||||
|
|
||||
|
@interface DTTimePeriodGroup : NSObject { |
||||
|
@protected |
||||
|
NSMutableArray *periods; |
||||
|
NSDate *StartDate; |
||||
|
NSDate *EndDate; |
||||
|
} |
||||
|
|
||||
|
@property (nonatomic, readonly) NSDate *StartDate; |
||||
|
@property (nonatomic, readonly) NSDate *EndDate; |
||||
|
|
||||
|
//Here we will use object subscripting to help create the illusion of an array
|
||||
|
- (id)objectAtIndexedSubscript:(NSUInteger)index; //getter
|
||||
|
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index; //setter
|
||||
|
|
||||
|
#pragma mark - Group Info |
||||
|
-(double)durationInYears; |
||||
|
-(double)durationInWeeks; |
||||
|
-(double)durationInDays; |
||||
|
-(double)durationInHours; |
||||
|
-(double)durationInMinutes; |
||||
|
-(double)durationInSeconds; |
||||
|
-(NSDate *)StartDate; |
||||
|
-(NSDate *)EndDate; |
||||
|
-(NSInteger)count; |
||||
|
|
||||
|
#pragma mark - Chain Time Manipulation |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size; |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size; |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; |
||||
|
|
||||
|
#pragma mark - Comparison |
||||
|
-(BOOL)hasSameCharacteristicsAs:(DTTimePeriodGroup *)group; |
||||
|
|
||||
|
#pragma mark - Updates |
||||
|
-(void)updateVariables; |
||||
|
@end |
@ -0,0 +1,234 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York |
||||
|
// |
||||
|
// Permission is hereby granted, free of charge, to any |
||||
|
// person obtaining a copy of this software and |
||||
|
// associated documentation files (the "Software"), to |
||||
|
// deal in the Software without restriction, including |
||||
|
// without limitation the rights to use, copy, modify, merge, |
||||
|
// publish, distribute, sublicense, and/or sell copies of the |
||||
|
// Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
// |
||||
|
// The above copyright notice and this permission notice shall |
||||
|
// be included in all copies or substantial portions of the Software. |
||||
|
// |
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
|
||||
|
#import "DTTimePeriodGroup.h" |
||||
|
#import "NSDate+DateTools.h" |
||||
|
|
||||
|
@interface DTTimePeriodGroup () |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@implementation DTTimePeriodGroup |
||||
|
|
||||
|
-(id) init |
||||
|
{ |
||||
|
if (self = [super init]) { |
||||
|
periods = [[NSMutableArray alloc] init]; |
||||
|
} |
||||
|
|
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (id)objectAtIndexedSubscript:(NSUInteger)index |
||||
|
{ |
||||
|
return periods[index]; |
||||
|
} |
||||
|
|
||||
|
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index { |
||||
|
periods[index] = obj; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Group Info |
||||
|
/** |
||||
|
* Returns the duration of the receiver in years |
||||
|
* |
||||
|
* @return NSInteger |
||||
|
*/ |
||||
|
-(double)durationInYears { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate yearsEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in weeks |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInWeeks { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate weeksEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in days |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInDays { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate daysEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in hours |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInHours { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate hoursEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in minutes |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInMinutes { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate minutesEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the duration of the receiver in seconds |
||||
|
* |
||||
|
* @return double |
||||
|
*/ |
||||
|
-(double)durationInSeconds { |
||||
|
if (self.StartDate && self.EndDate) { |
||||
|
return [self.StartDate secondsEarlierThan:self.EndDate]; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the NSDate representing the earliest date in the DTTimePeriodGroup (or subclass) |
||||
|
* |
||||
|
* @return NSDate |
||||
|
*/ |
||||
|
-(NSDate *)StartDate{ |
||||
|
return StartDate; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the NSDate representing the latest date in the DTTimePeriodGroup (or subclass) |
||||
|
* |
||||
|
* @return NSDate |
||||
|
*/ |
||||
|
-(NSDate *)EndDate{ |
||||
|
return EndDate; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The total number of DTTimePeriods in the group |
||||
|
* |
||||
|
* @return NSInteger |
||||
|
*/ |
||||
|
-(NSInteger)count{ |
||||
|
return periods.count; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a BOOL if the receiver and the comparison group have the same metadata (i.e. number of periods, start & end date, etc.) |
||||
|
* Returns YES if they share the same characteristics, otherwise NO |
||||
|
* |
||||
|
* @param group The group to compare with the receiver |
||||
|
* |
||||
|
* @return BOOL |
||||
|
*/ |
||||
|
-(BOOL)hasSameCharacteristicsAs:(DTTimePeriodGroup *)group{ |
||||
|
//Check characteristics first for speed |
||||
|
if (group.count != self.count) { |
||||
|
return NO; |
||||
|
} |
||||
|
else if (!group.StartDate && !group.EndDate && !self.StartDate && !self.EndDate){ |
||||
|
return YES; |
||||
|
} |
||||
|
else if (![group.StartDate isEqualToDate:self.StartDate] || ![group.EndDate isEqualToDate:self.EndDate]){ |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
return YES; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Chain Time Manipulation |
||||
|
/** |
||||
|
* Shifts all the time periods in the collection to an earlier date by the given size |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - The desired size of the shift |
||||
|
*/ |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size{ |
||||
|
[self shiftEarlierWithSize:size amount:1]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shifts all the time periods in the collection to an earlier date by the given size and amount. |
||||
|
* The amount acts as a multiplier to the size (i.e. "2 weeks" or "4 years") |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - The desired size of the shift |
||||
|
* @param amount NSInteger - Multiplier for the size |
||||
|
*/ |
||||
|
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ |
||||
|
if (periods) { |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
[((DTTimePeriod *)obj) shiftEarlierWithSize:size amount:amount]; |
||||
|
}]; |
||||
|
|
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shifts all the time periods in the collection to a later date by the given size |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - The desired size of the shift |
||||
|
*/ |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size{ |
||||
|
[self shiftLaterWithSize:size amount:1]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Shifts all the time periods in the collection to an later date by the given size and amount. |
||||
|
* The amount acts as a multiplier to the size (i.e. "2 weeks" or "4 years") |
||||
|
* |
||||
|
* @param size DTTimePeriodSize - The desired size of the shift |
||||
|
* @param amount NSInteger - Multiplier for the size |
||||
|
*/ |
||||
|
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ |
||||
|
if (periods) { |
||||
|
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { |
||||
|
[((DTTimePeriod *)obj) shiftLaterWithSize:size amount:amount]; |
||||
|
}]; |
||||
|
|
||||
|
[self updateVariables]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#pragma mark - Updates |
||||
|
-(void)updateVariables{} |
||||
|
@end |
@ -0,0 +1,29 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#import "DTConstants.h" |
||||
|
#import "DTError.h" |
||||
|
#import "NSDate+DateTools.h" |
||||
|
#import "DTTimePeriod.h" |
||||
|
#import "DTTimePeriodGroup.h" |
||||
|
#import "DTTimePeriodCollection.h" |
||||
|
#import "DTTimePeriodChain.h" |
@ -0,0 +1,194 @@ |
|||||
|
// Copyright (C) 2014 by Matthew York
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any
|
||||
|
// person obtaining a copy of this software and
|
||||
|
// associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge,
|
||||
|
// publish, distribute, sublicense, and/or sell copies of the
|
||||
|
// Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall
|
||||
|
// be included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#ifndef DateToolsLocalizedStrings |
||||
|
|
||||
|
#ifdef SPM |
||||
|
#define DateToolsLocalizedStrings(key) \ |
||||
|
NSLocalizedStringFromTableInBundle(key, @"DateTools", [NSBundle bundleWithPath:[[SWIFTPM_MODULE_BUNDLE resourcePath] stringByAppendingPathComponent:@"DateTools.bundle"]], nil) |
||||
|
#else |
||||
|
#define DateToolsLocalizedStrings(key) \ |
||||
|
NSLocalizedStringFromTableInBundle(key, @"DateTools", [NSBundle bundleWithPath:[[[NSBundle bundleForClass:[DTError class]] resourcePath] stringByAppendingPathComponent:@"DateTools.bundle"]], nil) |
||||
|
#endif |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "DTConstants.h" |
||||
|
|
||||
|
@interface NSDate (DateTools) |
||||
|
|
||||
|
#pragma mark - Time Ago |
||||
|
+ (NSString*)timeAgoSinceDate:(NSDate*)date; |
||||
|
+ (NSString*)shortTimeAgoSinceDate:(NSDate*)date; |
||||
|
+ (NSString *)weekTimeAgoSinceDate:(NSDate *)date; |
||||
|
|
||||
|
- (NSString*)timeAgoSinceNow; |
||||
|
- (NSString *)shortTimeAgoSinceNow; |
||||
|
- (NSString *)weekTimeAgoSinceNow; |
||||
|
|
||||
|
- (NSString *)timeAgoSinceDate:(NSDate *)date; |
||||
|
- (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates; |
||||
|
- (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates numericTimes:(BOOL)useNumericTimes; |
||||
|
|
||||
|
|
||||
|
- (NSString *)shortTimeAgoSinceDate:(NSDate *)date; |
||||
|
- (NSString *)weekTimeAgoSinceDate:(NSDate *)date; |
||||
|
|
||||
|
|
||||
|
#pragma mark - Date Components Without Calendar |
||||
|
- (NSInteger)era; |
||||
|
- (NSInteger)year; |
||||
|
- (NSInteger)month; |
||||
|
- (NSInteger)day; |
||||
|
- (NSInteger)hour; |
||||
|
- (NSInteger)minute; |
||||
|
- (NSInteger)second; |
||||
|
- (NSInteger)weekday; |
||||
|
- (NSInteger)weekdayOrdinal; |
||||
|
- (NSInteger)quarter; |
||||
|
- (NSInteger)weekOfMonth; |
||||
|
- (NSInteger)weekOfYear; |
||||
|
- (NSInteger)yearForWeekOfYear; |
||||
|
- (NSInteger)daysInMonth; |
||||
|
- (NSInteger)dayOfYear; |
||||
|
-(NSInteger)daysInYear; |
||||
|
-(BOOL)isInLeapYear; |
||||
|
- (BOOL)isToday; |
||||
|
- (BOOL)isTomorrow; |
||||
|
-(BOOL)isYesterday; |
||||
|
- (BOOL)isWeekend; |
||||
|
-(BOOL)isSameDay:(NSDate *)date; |
||||
|
+ (BOOL)isSameDay:(NSDate *)date asDate:(NSDate *)compareDate; |
||||
|
|
||||
|
#pragma mark - Date Components With Calendar |
||||
|
|
||||
|
|
||||
|
- (NSInteger)eraWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)yearWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)monthWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)dayWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)hourWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)minuteWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)secondWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)weekdayWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)weekdayOrdinalWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)quarterWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)weekOfMonthWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)weekOfYearWithCalendar:(NSCalendar *)calendar; |
||||
|
- (NSInteger)yearForWeekOfYearWithCalendar:(NSCalendar *)calendar; |
||||
|
|
||||
|
|
||||
|
#pragma mark - Date Creating |
||||
|
+ (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day; |
||||
|
+ (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second; |
||||
|
+ (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString; |
||||
|
+ (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString timeZone:(NSTimeZone *)timeZone; |
||||
|
|
||||
|
|
||||
|
#pragma mark - Date Editing |
||||
|
#pragma mark Date By Adding |
||||
|
- (NSDate *)dateByAddingYears:(NSInteger)years; |
||||
|
- (NSDate *)dateByAddingMonths:(NSInteger)months; |
||||
|
- (NSDate *)dateByAddingWeeks:(NSInteger)weeks; |
||||
|
- (NSDate *)dateByAddingDays:(NSInteger)days; |
||||
|
- (NSDate *)dateByAddingHours:(NSInteger)hours; |
||||
|
- (NSDate *)dateByAddingMinutes:(NSInteger)minutes; |
||||
|
- (NSDate *)dateByAddingSeconds:(NSInteger)seconds; |
||||
|
#pragma mark Date By Subtracting |
||||
|
- (NSDate *)dateBySubtractingYears:(NSInteger)years; |
||||
|
- (NSDate *)dateBySubtractingMonths:(NSInteger)months; |
||||
|
- (NSDate *)dateBySubtractingWeeks:(NSInteger)weeks; |
||||
|
- (NSDate *)dateBySubtractingDays:(NSInteger)days; |
||||
|
- (NSDate *)dateBySubtractingHours:(NSInteger)hours; |
||||
|
- (NSDate *)dateBySubtractingMinutes:(NSInteger)minutes; |
||||
|
- (NSDate *)dateBySubtractingSeconds:(NSInteger)seconds; |
||||
|
|
||||
|
#pragma mark - Date Comparison |
||||
|
#pragma mark Time From |
||||
|
-(NSInteger)yearsFrom:(NSDate *)date; |
||||
|
-(NSInteger)monthsFrom:(NSDate *)date; |
||||
|
-(NSInteger)weeksFrom:(NSDate *)date; |
||||
|
-(NSInteger)daysFrom:(NSDate *)date; |
||||
|
-(double)hoursFrom:(NSDate *)date; |
||||
|
-(double)minutesFrom:(NSDate *)date; |
||||
|
-(double)secondsFrom:(NSDate *)date; |
||||
|
#pragma mark Time From With Calendar |
||||
|
-(NSInteger)yearsFrom:(NSDate *)date calendar:(NSCalendar *)calendar; |
||||
|
-(NSInteger)monthsFrom:(NSDate *)date calendar:(NSCalendar *)calendar; |
||||
|
-(NSInteger)weeksFrom:(NSDate *)date calendar:(NSCalendar *)calendar; |
||||
|
-(NSInteger)daysFrom:(NSDate *)date calendar:(NSCalendar *)calendar; |
||||
|
|
||||
|
#pragma mark Time Until |
||||
|
-(NSInteger)yearsUntil; |
||||
|
-(NSInteger)monthsUntil; |
||||
|
-(NSInteger)weeksUntil; |
||||
|
-(NSInteger)daysUntil; |
||||
|
-(double)hoursUntil; |
||||
|
-(double)minutesUntil; |
||||
|
-(double)secondsUntil; |
||||
|
#pragma mark Time Ago |
||||
|
-(NSInteger)yearsAgo; |
||||
|
-(NSInteger)monthsAgo; |
||||
|
-(NSInteger)weeksAgo; |
||||
|
-(NSInteger)daysAgo; |
||||
|
-(double)hoursAgo; |
||||
|
-(double)minutesAgo; |
||||
|
-(double)secondsAgo; |
||||
|
#pragma mark Earlier Than |
||||
|
-(NSInteger)yearsEarlierThan:(NSDate *)date; |
||||
|
-(NSInteger)monthsEarlierThan:(NSDate *)date; |
||||
|
-(NSInteger)weeksEarlierThan:(NSDate *)date; |
||||
|
-(NSInteger)daysEarlierThan:(NSDate *)date; |
||||
|
-(double)hoursEarlierThan:(NSDate *)date; |
||||
|
-(double)minutesEarlierThan:(NSDate *)date; |
||||
|
-(double)secondsEarlierThan:(NSDate *)date; |
||||
|
#pragma mark Later Than |
||||
|
-(NSInteger)yearsLaterThan:(NSDate *)date; |
||||
|
-(NSInteger)monthsLaterThan:(NSDate *)date; |
||||
|
-(NSInteger)weeksLaterThan:(NSDate *)date; |
||||
|
-(NSInteger)daysLaterThan:(NSDate *)date; |
||||
|
-(double)hoursLaterThan:(NSDate *)date; |
||||
|
-(double)minutesLaterThan:(NSDate *)date; |
||||
|
-(double)secondsLaterThan:(NSDate *)date; |
||||
|
#pragma mark Comparators |
||||
|
-(BOOL)isEarlierThan:(NSDate *)date; |
||||
|
-(BOOL)isLaterThan:(NSDate *)date; |
||||
|
-(BOOL)isEarlierThanOrEqualTo:(NSDate *)date; |
||||
|
-(BOOL)isLaterThanOrEqualTo:(NSDate *)date; |
||||
|
|
||||
|
#pragma mark - Formatted Dates |
||||
|
#pragma mark Formatted With Style |
||||
|
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style; |
||||
|
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone; |
||||
|
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style locale:(NSLocale *)locale; |
||||
|
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale; |
||||
|
#pragma mark Formatted With Format |
||||
|
-(NSString *)formattedDateWithFormat:(NSString *)format; |
||||
|
-(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone; |
||||
|
-(NSString *)formattedDateWithFormat:(NSString *)format locale:(NSLocale *)locale; |
||||
|
-(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale; |
||||
|
|
||||
|
#pragma mark - Helpers |
||||
|
+(NSString *)defaultCalendarIdentifier; |
||||
|
+ (void)setDefaultCalendarIdentifier:(NSString *)identifier; |
||||
|
@end |
File diff suppressed because it is too large
@ -0,0 +1,27 @@ |
|||||
|
//
|
||||
|
// MJExtension.h
|
||||
|
// MJExtension
|
||||
|
//
|
||||
|
// Created by mj on 14-1-15.
|
||||
|
// Copyright (c) 2014年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "NSObject+MJCoding.h" |
||||
|
#import "NSObject+MJProperty.h" |
||||
|
#import "NSObject+MJClass.h" |
||||
|
#import "NSObject+MJKeyValue.h" |
||||
|
#import "NSString+MJExtension.h" |
||||
|
#import "MJExtensionConst.h" |
||||
|
|
||||
|
#import "MJFoundation.h" |
||||
|
|
||||
|
//! Project version number for MJExtension.
|
||||
|
FOUNDATION_EXPORT double MJExtensionVersionNumber; |
||||
|
|
||||
|
//! Project version string for MJExtension.
|
||||
|
FOUNDATION_EXPORT const unsigned char MJExtensionVersionString[]; |
||||
|
|
||||
|
// In this header, you should import all the public headers of your framework using statements like #import <MJExtension/PublicHeader.h>
|
||||
|
|
||||
|
|
@ -0,0 +1,111 @@ |
|||||
|
|
||||
|
#ifndef __MJExtensionConst__H__ |
||||
|
#define __MJExtensionConst__H__ |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
#ifndef MJ_LOCK |
||||
|
#define MJ_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); |
||||
|
#endif |
||||
|
|
||||
|
#ifndef MJ_UNLOCK |
||||
|
#define MJ_UNLOCK(lock) dispatch_semaphore_signal(lock); |
||||
|
#endif |
||||
|
|
||||
|
// 信号量
|
||||
|
#define MJExtensionSemaphoreCreate \ |
||||
|
extern dispatch_semaphore_t mje_signalSemaphore; \ |
||||
|
extern dispatch_once_t mje_onceTokenSemaphore; \ |
||||
|
dispatch_once(&mje_onceTokenSemaphore, ^{ \ |
||||
|
mje_signalSemaphore = dispatch_semaphore_create(1); \ |
||||
|
}); |
||||
|
|
||||
|
// 过期
|
||||
|
#define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) |
||||
|
|
||||
|
// 构建错误
|
||||
|
#define MJExtensionBuildError(clazz, msg) \ |
||||
|
NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \ |
||||
|
[clazz setMj_error:error]; |
||||
|
|
||||
|
// 日志输出
|
||||
|
#ifdef DEBUG |
||||
|
#define MJExtensionLog(...) NSLog(__VA_ARGS__) |
||||
|
#else |
||||
|
#define MJExtensionLog(...) |
||||
|
#endif |
||||
|
|
||||
|
/**
|
||||
|
* 断言 |
||||
|
* @param condition 条件 |
||||
|
* @param returnValue 返回值 |
||||
|
*/ |
||||
|
#define MJExtensionAssertError(condition, returnValue, clazz, msg) \ |
||||
|
[clazz setMj_error:nil]; \ |
||||
|
if ((condition) == NO) { \ |
||||
|
MJExtensionBuildError(clazz, msg); \ |
||||
|
return returnValue;\ |
||||
|
} |
||||
|
|
||||
|
#define MJExtensionAssert2(condition, returnValue) \ |
||||
|
if ((condition) == NO) return returnValue; |
||||
|
|
||||
|
/**
|
||||
|
* 断言 |
||||
|
* @param condition 条件 |
||||
|
*/ |
||||
|
#define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) |
||||
|
|
||||
|
/**
|
||||
|
* 断言 |
||||
|
* @param param 参数 |
||||
|
* @param returnValue 返回值 |
||||
|
*/ |
||||
|
#define MJExtensionAssertParamNotNil2(param, returnValue) \ |
||||
|
MJExtensionAssert2((param) != nil, returnValue) |
||||
|
|
||||
|
/**
|
||||
|
* 断言 |
||||
|
* @param param 参数 |
||||
|
*/ |
||||
|
#define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) |
||||
|
|
||||
|
/**
|
||||
|
* 打印所有的属性 |
||||
|
*/ |
||||
|
#define MJLogAllIvars \ |
||||
|
- (NSString *)description \ |
||||
|
{ \ |
||||
|
return [self mj_keyValues].description; \ |
||||
|
} |
||||
|
#define MJExtensionLogAllProperties MJLogAllIvars |
||||
|
|
||||
|
/** 仅在 Debugger 展示所有的属性 */ |
||||
|
#define MJImplementDebugDescription \ |
||||
|
- (NSString *)debugDescription \ |
||||
|
{ \ |
||||
|
return [self mj_keyValues].debugDescription; \ |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* 类型(属性类型) |
||||
|
*/ |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeInt; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeShort; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeFloat; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeDouble; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeLong; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeLongLong; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeChar; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL1; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL2; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypePointer; |
||||
|
|
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeIvar; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeMethod; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeBlock; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeClass; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeSEL; |
||||
|
FOUNDATION_EXPORT NSString *const MJPropertyTypeId; |
||||
|
|
||||
|
#endif |
@ -0,0 +1,27 @@ |
|||||
|
#ifndef __MJExtensionConst__M__ |
||||
|
#define __MJExtensionConst__M__ |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/** |
||||
|
* 成员变量类型(属性类型) |
||||
|
*/ |
||||
|
NSString *const MJPropertyTypeInt = @"i"; |
||||
|
NSString *const MJPropertyTypeShort = @"s"; |
||||
|
NSString *const MJPropertyTypeFloat = @"f"; |
||||
|
NSString *const MJPropertyTypeDouble = @"d"; |
||||
|
NSString *const MJPropertyTypeLong = @"l"; |
||||
|
NSString *const MJPropertyTypeLongLong = @"q"; |
||||
|
NSString *const MJPropertyTypeChar = @"c"; |
||||
|
NSString *const MJPropertyTypeBOOL1 = @"c"; |
||||
|
NSString *const MJPropertyTypeBOOL2 = @"b"; |
||||
|
NSString *const MJPropertyTypePointer = @"*"; |
||||
|
|
||||
|
NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}"; |
||||
|
NSString *const MJPropertyTypeMethod = @"^{objc_method=}"; |
||||
|
NSString *const MJPropertyTypeBlock = @"@?"; |
||||
|
NSString *const MJPropertyTypeClass = @"#"; |
||||
|
NSString *const MJPropertyTypeSEL = @":"; |
||||
|
NSString *const MJPropertyTypeId = @"@"; |
||||
|
|
||||
|
#endif |
@ -0,0 +1,16 @@ |
|||||
|
//
|
||||
|
// MJFoundation.h
|
||||
|
// MJExtensionExample
|
||||
|
//
|
||||
|
// Created by MJ Lee on 14/7/16.
|
||||
|
// Copyright (c) 2014年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@interface MJFoundation : NSObject |
||||
|
|
||||
|
+ (BOOL)isClassFromFoundation:(Class)c; |
||||
|
+ (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName; |
||||
|
|
||||
|
@end |
@ -0,0 +1,70 @@ |
|||||
|
// |
||||
|
// MJFoundation.m |
||||
|
// MJExtensionExample |
||||
|
// |
||||
|
// Created by MJ Lee on 14/7/16. |
||||
|
// Copyright (c) 2014年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "MJFoundation.h" |
||||
|
#import "MJExtensionConst.h" |
||||
|
#import <CoreData/CoreData.h> |
||||
|
#import "objc/runtime.h" |
||||
|
|
||||
|
@implementation MJFoundation |
||||
|
|
||||
|
+ (BOOL)isClassFromFoundation:(Class)c |
||||
|
{ |
||||
|
if (c == [NSObject class] || c == [NSManagedObject class]) return YES; |
||||
|
|
||||
|
static NSSet *foundationClasses; |
||||
|
static dispatch_once_t onceToken; |
||||
|
dispatch_once(&onceToken, ^{ |
||||
|
// 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断 |
||||
|
foundationClasses = [NSSet setWithObjects: |
||||
|
[NSURL class], |
||||
|
[NSDate class], |
||||
|
[NSValue class], |
||||
|
[NSData class], |
||||
|
[NSError class], |
||||
|
[NSArray class], |
||||
|
[NSDictionary class], |
||||
|
[NSString class], |
||||
|
[NSAttributedString class], nil]; |
||||
|
}); |
||||
|
|
||||
|
__block BOOL result = NO; |
||||
|
[foundationClasses enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) { |
||||
|
if ([c isSubclassOfClass:foundationClass]) { |
||||
|
result = YES; |
||||
|
*stop = YES; |
||||
|
} |
||||
|
}]; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
+ (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName |
||||
|
{ |
||||
|
if (!propertyName) return NO; |
||||
|
|
||||
|
static NSSet<NSString *> *objectProtocolPropertyNames; |
||||
|
static dispatch_once_t onceToken; |
||||
|
dispatch_once(&onceToken, ^{ |
||||
|
unsigned int count = 0; |
||||
|
objc_property_t *propertyList = protocol_copyPropertyList(@protocol(NSObject), &count); |
||||
|
NSMutableSet *propertyNames = [NSMutableSet setWithCapacity:count]; |
||||
|
for (int i = 0; i < count; i++) { |
||||
|
objc_property_t property = propertyList[i]; |
||||
|
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; |
||||
|
if (propertyName) { |
||||
|
[propertyNames addObject:propertyName]; |
||||
|
} |
||||
|
} |
||||
|
objectProtocolPropertyNames = [propertyNames copy]; |
||||
|
free(propertyList); |
||||
|
}); |
||||
|
|
||||
|
return [objectProtocolPropertyNames containsObject:propertyName]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,53 @@ |
|||||
|
//
|
||||
|
// MJProperty.h
|
||||
|
// MJExtensionExample
|
||||
|
//
|
||||
|
// Created by MJ Lee on 15/4/17.
|
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
|
// 包装一个成员属性
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import <objc/runtime.h> |
||||
|
#import "MJPropertyType.h" |
||||
|
#import "MJPropertyKey.h" |
||||
|
|
||||
|
/**
|
||||
|
* 包装一个成员 |
||||
|
*/ |
||||
|
@interface MJProperty : NSObject |
||||
|
/** 成员属性 */ |
||||
|
@property (nonatomic, assign) objc_property_t property; |
||||
|
/** 成员属性的名字 */ |
||||
|
@property (nonatomic, readonly) NSString *name; |
||||
|
|
||||
|
/** 成员属性的类型 */ |
||||
|
@property (nonatomic, readonly) MJPropertyType *type; |
||||
|
/** 成员属性来源于哪个类(可能是父类) */ |
||||
|
@property (nonatomic, assign) Class srcClass; |
||||
|
|
||||
|
/**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/ |
||||
|
/** 设置最原始的key */ |
||||
|
- (void)setOriginKey:(id)originKey forClass:(Class)c; |
||||
|
/** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */ |
||||
|
- (NSArray *)propertyKeysForClass:(Class)c; |
||||
|
|
||||
|
/** 模型数组中的模型类型 */ |
||||
|
- (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c; |
||||
|
- (Class)objectClassInArrayForClass:(Class)c; |
||||
|
/**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/ |
||||
|
|
||||
|
/**
|
||||
|
* 设置object的成员变量值 |
||||
|
*/ |
||||
|
- (void)setValue:(id)value forObject:(id)object; |
||||
|
/**
|
||||
|
* 得到object的成员属性值 |
||||
|
*/ |
||||
|
- (id)valueForObject:(id)object; |
||||
|
|
||||
|
/**
|
||||
|
* 初始化 |
||||
|
*/ |
||||
|
+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property; |
||||
|
|
||||
|
@end |
@ -0,0 +1,211 @@ |
|||||
|
// |
||||
|
// MJProperty.m |
||||
|
// MJExtensionExample |
||||
|
// |
||||
|
// Created by MJ Lee on 15/4/17. |
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "MJProperty.h" |
||||
|
#import "MJFoundation.h" |
||||
|
#import "MJExtensionConst.h" |
||||
|
#import <objc/message.h> |
||||
|
#include "TargetConditionals.h" |
||||
|
|
||||
|
@interface MJProperty() |
||||
|
@property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; |
||||
|
@property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; |
||||
|
@property (strong, nonatomic) dispatch_semaphore_t propertyKeysLock; |
||||
|
@property (strong, nonatomic) dispatch_semaphore_t objectClassInArrayLock; |
||||
|
@end |
||||
|
|
||||
|
@implementation MJProperty |
||||
|
|
||||
|
#pragma mark - 初始化 |
||||
|
- (instancetype)init |
||||
|
{ |
||||
|
if (self = [super init]) { |
||||
|
_propertyKeysDict = [NSMutableDictionary dictionary]; |
||||
|
_objectClassInArrayDict = [NSMutableDictionary dictionary]; |
||||
|
_propertyKeysLock = dispatch_semaphore_create(1); |
||||
|
_objectClassInArrayLock = dispatch_semaphore_create(1); |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 缓存 |
||||
|
+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property |
||||
|
{ |
||||
|
MJProperty *propertyObj = objc_getAssociatedObject(self, property); |
||||
|
if (propertyObj == nil) { |
||||
|
propertyObj = [[self alloc] init]; |
||||
|
propertyObj.property = property; |
||||
|
objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
||||
|
} |
||||
|
return propertyObj; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 公共方法 |
||||
|
- (void)setProperty:(objc_property_t)property |
||||
|
{ |
||||
|
_property = property; |
||||
|
|
||||
|
MJExtensionAssertParamNotNil(property); |
||||
|
|
||||
|
// 1.属性名 |
||||
|
_name = @(property_getName(property)); |
||||
|
|
||||
|
// 2.成员类型 |
||||
|
NSString *attrs = @(property_getAttributes(property)); |
||||
|
NSUInteger dotLoc = [attrs rangeOfString:@","].location; |
||||
|
NSString *code = nil; |
||||
|
NSUInteger loc = 1; |
||||
|
if (dotLoc == NSNotFound) { // 没有, |
||||
|
code = [attrs substringFromIndex:loc]; |
||||
|
} else { |
||||
|
code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; |
||||
|
} |
||||
|
_type = [MJPropertyType cachedTypeWithCode:code]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获得成员变量的值 |
||||
|
*/ |
||||
|
- (id)valueForObject:(id)object |
||||
|
{ |
||||
|
if (self.type.KVCDisabled) return [NSNull null]; |
||||
|
|
||||
|
id value = [object valueForKey:self.name]; |
||||
|
|
||||
|
// 32位BOOL类型转换json后成Int类型 |
||||
|
/** https://github.com/CoderMJLee/MJExtension/issues/545 */ |
||||
|
// 32 bit device OR 32 bit Simulator |
||||
|
#if defined(__arm__) || (TARGET_OS_SIMULATOR && !__LP64__) |
||||
|
if (self.type.isBoolType) { |
||||
|
value = @([(NSNumber *)value boolValue]); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置成员变量的值 |
||||
|
*/ |
||||
|
- (void)setValue:(id)value forObject:(id)object |
||||
|
{ |
||||
|
if (self.type.KVCDisabled || value == nil) return; |
||||
|
[object setValue:value forKey:self.name]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过字符串key创建对应的keys |
||||
|
*/ |
||||
|
- (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey |
||||
|
{ |
||||
|
if (stringKey.length == 0) return nil; |
||||
|
|
||||
|
NSMutableArray *propertyKeys = [NSMutableArray array]; |
||||
|
// 如果有多级映射 |
||||
|
NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; |
||||
|
|
||||
|
for (NSString *oldKey in oldKeys) { |
||||
|
NSUInteger start = [oldKey rangeOfString:@"["].location; |
||||
|
if (start != NSNotFound) { // 有索引的key |
||||
|
NSString *prefixKey = [oldKey substringToIndex:start]; |
||||
|
NSString *indexKey = prefixKey; |
||||
|
if (prefixKey.length) { |
||||
|
MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; |
||||
|
propertyKey.name = prefixKey; |
||||
|
[propertyKeys addObject:propertyKey]; |
||||
|
|
||||
|
indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; |
||||
|
} |
||||
|
|
||||
|
/** 解析索引 **/ |
||||
|
// 元素 |
||||
|
NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; |
||||
|
for (NSInteger i = 0; i<cmps.count - 1; i++) { |
||||
|
MJPropertyKey *subPropertyKey = [[MJPropertyKey alloc] init]; |
||||
|
subPropertyKey.type = MJPropertyKeyTypeArray; |
||||
|
subPropertyKey.name = cmps[i]; |
||||
|
[propertyKeys addObject:subPropertyKey]; |
||||
|
} |
||||
|
} else { // 没有索引的key |
||||
|
MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; |
||||
|
propertyKey.name = oldKey; |
||||
|
[propertyKeys addObject:propertyKey]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return propertyKeys; |
||||
|
} |
||||
|
|
||||
|
/** 对应着字典中的key */ |
||||
|
- (void)setOriginKey:(id)originKey forClass:(Class)c |
||||
|
{ |
||||
|
if ([originKey isKindOfClass:[NSString class]]) { // 字符串类型的key |
||||
|
NSArray *propertyKeys = [self propertyKeysWithStringKey:originKey]; |
||||
|
if (propertyKeys.count) { |
||||
|
[self setPropertyKeys:@[propertyKeys] forClass:c]; |
||||
|
} |
||||
|
} else if ([originKey isKindOfClass:[NSArray class]]) { |
||||
|
NSMutableArray *keyses = [NSMutableArray array]; |
||||
|
for (NSString *stringKey in originKey) { |
||||
|
NSArray *propertyKeys = [self propertyKeysWithStringKey:stringKey]; |
||||
|
if (propertyKeys.count) { |
||||
|
[keyses addObject:propertyKeys]; |
||||
|
} |
||||
|
} |
||||
|
if (keyses.count) { |
||||
|
[self setPropertyKeys:keyses forClass:c]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** 对应着字典中的多级key */ |
||||
|
- (void)setPropertyKeys:(NSArray *)propertyKeys forClass:(Class)c |
||||
|
{ |
||||
|
if (propertyKeys.count == 0) return; |
||||
|
NSString *key = NSStringFromClass(c); |
||||
|
if (!key) return; |
||||
|
|
||||
|
MJ_LOCK(self.propertyKeysLock); |
||||
|
self.propertyKeysDict[key] = propertyKeys; |
||||
|
MJ_UNLOCK(self.propertyKeysLock); |
||||
|
} |
||||
|
|
||||
|
- (NSArray *)propertyKeysForClass:(Class)c |
||||
|
{ |
||||
|
NSString *key = NSStringFromClass(c); |
||||
|
if (!key) return nil; |
||||
|
|
||||
|
MJ_LOCK(self.propertyKeysLock); |
||||
|
NSArray *propertyKeys = self.propertyKeysDict[key]; |
||||
|
MJ_UNLOCK(self.propertyKeysLock); |
||||
|
return propertyKeys; |
||||
|
} |
||||
|
|
||||
|
/** 模型数组中的模型类型 */ |
||||
|
- (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c |
||||
|
{ |
||||
|
if (!objectClass) return; |
||||
|
NSString *key = NSStringFromClass(c); |
||||
|
if (!key) return; |
||||
|
|
||||
|
MJ_LOCK(self.objectClassInArrayLock); |
||||
|
self.objectClassInArrayDict[key] = objectClass; |
||||
|
MJ_UNLOCK(self.objectClassInArrayLock); |
||||
|
} |
||||
|
|
||||
|
- (Class)objectClassInArrayForClass:(Class)c |
||||
|
{ |
||||
|
NSString *key = NSStringFromClass(c); |
||||
|
if (!key) return nil; |
||||
|
|
||||
|
MJ_LOCK(self.objectClassInArrayLock); |
||||
|
Class objectClass = self.objectClassInArrayDict[key]; |
||||
|
MJ_UNLOCK(self.objectClassInArrayLock); |
||||
|
return objectClass; |
||||
|
} |
||||
|
@end |
@ -0,0 +1,30 @@ |
|||||
|
//
|
||||
|
// MJPropertyKey.h
|
||||
|
// MJExtensionExample
|
||||
|
//
|
||||
|
// Created by MJ Lee on 15/8/11.
|
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
typedef enum { |
||||
|
MJPropertyKeyTypeDictionary = 0, // 字典的key
|
||||
|
MJPropertyKeyTypeArray // 数组的key
|
||||
|
} MJPropertyKeyType; |
||||
|
|
||||
|
/**
|
||||
|
* 属性的key |
||||
|
*/ |
||||
|
@interface MJPropertyKey : NSObject |
||||
|
/** key的名字 */ |
||||
|
@property (copy, nonatomic) NSString *name; |
||||
|
/** key的种类,可能是@"10",可能是@"age" */ |
||||
|
@property (assign, nonatomic) MJPropertyKeyType type; |
||||
|
|
||||
|
/**
|
||||
|
* 根据当前的key,也就是name,从object(字典或者数组)中取值 |
||||
|
*/ |
||||
|
- (id)valueInObject:(id)object; |
||||
|
|
||||
|
@end |
@ -0,0 +1,25 @@ |
|||||
|
// |
||||
|
// MJPropertyKey.m |
||||
|
// MJExtensionExample |
||||
|
// |
||||
|
// Created by MJ Lee on 15/8/11. |
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "MJPropertyKey.h" |
||||
|
|
||||
|
@implementation MJPropertyKey |
||||
|
|
||||
|
- (id)valueInObject:(id)object |
||||
|
{ |
||||
|
if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) { |
||||
|
return object[self.name]; |
||||
|
} else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) { |
||||
|
NSArray *array = object; |
||||
|
NSUInteger index = self.name.intValue; |
||||
|
if (index < array.count) return array[index]; |
||||
|
return nil; |
||||
|
} |
||||
|
return nil; |
||||
|
} |
||||
|
@end |
@ -0,0 +1,39 @@ |
|||||
|
//
|
||||
|
// MJPropertyType.h
|
||||
|
// MJExtension
|
||||
|
//
|
||||
|
// Created by mj on 14-1-15.
|
||||
|
// Copyright (c) 2014年 小码哥. All rights reserved.
|
||||
|
// 包装一种类型
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/**
|
||||
|
* 包装一种类型 |
||||
|
*/ |
||||
|
@interface MJPropertyType : NSObject |
||||
|
/** 类型标识符 */ |
||||
|
@property (nonatomic, copy) NSString *code; |
||||
|
|
||||
|
/** 是否为id类型 */ |
||||
|
@property (nonatomic, readonly, getter=isIdType) BOOL idType; |
||||
|
|
||||
|
/** 是否为基本数字类型:int、float等 */ |
||||
|
@property (nonatomic, readonly, getter=isNumberType) BOOL numberType; |
||||
|
|
||||
|
/** 是否为BOOL类型 */ |
||||
|
@property (nonatomic, readonly, getter=isBoolType) BOOL boolType; |
||||
|
|
||||
|
/** 对象类型(如果是基本数据类型,此值为nil) */ |
||||
|
@property (nonatomic, readonly) Class typeClass; |
||||
|
|
||||
|
/** 类型是否来自于Foundation框架,比如NSString、NSArray */ |
||||
|
@property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation; |
||||
|
/** 类型是否不支持KVC */ |
||||
|
@property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled; |
||||
|
|
||||
|
/**
|
||||
|
* 获得缓存的类型对象 |
||||
|
*/ |
||||
|
+ (instancetype)cachedTypeWithCode:(NSString *)code; |
||||
|
@end |
@ -0,0 +1,71 @@ |
|||||
|
// |
||||
|
// MJPropertyType.m |
||||
|
// MJExtension |
||||
|
// |
||||
|
// Created by mj on 14-1-15. |
||||
|
// Copyright (c) 2014年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "MJPropertyType.h" |
||||
|
#import "MJExtension.h" |
||||
|
#import "MJFoundation.h" |
||||
|
#import "MJExtensionConst.h" |
||||
|
|
||||
|
@implementation MJPropertyType |
||||
|
|
||||
|
+ (instancetype)cachedTypeWithCode:(NSString *)code |
||||
|
{ |
||||
|
MJExtensionAssertParamNotNil2(code, nil); |
||||
|
|
||||
|
static NSMutableDictionary *types; |
||||
|
static dispatch_once_t onceToken; |
||||
|
dispatch_once(&onceToken, ^{ |
||||
|
types = [NSMutableDictionary dictionary]; |
||||
|
}); |
||||
|
|
||||
|
MJPropertyType *type = types[code]; |
||||
|
if (type == nil) { |
||||
|
type = [[self alloc] init]; |
||||
|
type.code = code; |
||||
|
types[code] = type; |
||||
|
} |
||||
|
return type; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 公共方法 |
||||
|
- (void)setCode:(NSString *)code |
||||
|
{ |
||||
|
_code = code; |
||||
|
|
||||
|
MJExtensionAssertParamNotNil(code); |
||||
|
|
||||
|
if ([code isEqualToString:MJPropertyTypeId]) { |
||||
|
_idType = YES; |
||||
|
} else if (code.length == 0) { |
||||
|
_KVCDisabled = YES; |
||||
|
} else if (code.length > 3 && [code hasPrefix:@"@\""]) { |
||||
|
// 去掉@"和",截取中间的类型名称 |
||||
|
_code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; |
||||
|
_typeClass = NSClassFromString(_code); |
||||
|
_fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; |
||||
|
_numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; |
||||
|
|
||||
|
} else if ([code isEqualToString:MJPropertyTypeSEL] || |
||||
|
[code isEqualToString:MJPropertyTypeIvar] || |
||||
|
[code isEqualToString:MJPropertyTypeMethod]) { |
||||
|
_KVCDisabled = YES; |
||||
|
} |
||||
|
|
||||
|
// 是否为数字类型 |
||||
|
NSString *lowerCode = _code.lowercaseString; |
||||
|
NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; |
||||
|
if ([numberTypes containsObject:lowerCode]) { |
||||
|
_numberType = YES; |
||||
|
|
||||
|
if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] |
||||
|
|| [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { |
||||
|
_boolType = YES; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
@end |
@ -0,0 +1,90 @@ |
|||||
|
//
|
||||
|
// NSObject+MJClass.h
|
||||
|
// MJExtensionExample
|
||||
|
//
|
||||
|
// Created by MJ Lee on 15/8/11.
|
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/**
|
||||
|
* 遍历所有类的block(父类) |
||||
|
*/ |
||||
|
typedef void (^MJClassesEnumeration)(Class c, BOOL *stop); |
||||
|
|
||||
|
/** 这个数组中的属性名才会进行字典和模型的转换 */ |
||||
|
typedef NSArray * (^MJAllowedPropertyNames)(void); |
||||
|
/** 这个数组中的属性名才会进行归档 */ |
||||
|
typedef NSArray * (^MJAllowedCodingPropertyNames)(void); |
||||
|
|
||||
|
/** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */ |
||||
|
typedef NSArray * (^MJIgnoredPropertyNames)(void); |
||||
|
/** 这个数组中的属性名将会被忽略:不进行归档 */ |
||||
|
typedef NSArray * (^MJIgnoredCodingPropertyNames)(void); |
||||
|
|
||||
|
/**
|
||||
|
* 类相关的扩展 |
||||
|
*/ |
||||
|
@interface NSObject (MJClass) |
||||
|
/**
|
||||
|
* 遍历所有的类 |
||||
|
*/ |
||||
|
+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration; |
||||
|
+ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration; |
||||
|
|
||||
|
#pragma mark - 属性白名单配置 |
||||
|
/**
|
||||
|
* 这个数组中的属性名才会进行字典和模型的转换 |
||||
|
* |
||||
|
* @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 |
||||
|
*/ |
||||
|
+ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; |
||||
|
|
||||
|
/**
|
||||
|
* 这个数组中的属性名才会进行字典和模型的转换 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_totalAllowedPropertyNames; |
||||
|
|
||||
|
#pragma mark - 属性黑名单配置 |
||||
|
/**
|
||||
|
* 这个数组中的属性名将会被忽略:不进行字典和模型的转换 |
||||
|
* |
||||
|
* @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 |
||||
|
*/ |
||||
|
+ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; |
||||
|
|
||||
|
/**
|
||||
|
* 这个数组中的属性名将会被忽略:不进行字典和模型的转换 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_totalIgnoredPropertyNames; |
||||
|
|
||||
|
#pragma mark - 归档属性白名单配置 |
||||
|
/**
|
||||
|
* 这个数组中的属性名才会进行归档 |
||||
|
* |
||||
|
* @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 |
||||
|
*/ |
||||
|
+ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; |
||||
|
|
||||
|
/**
|
||||
|
* 这个数组中的属性名才会进行字典和模型的转换 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_totalAllowedCodingPropertyNames; |
||||
|
|
||||
|
#pragma mark - 归档属性黑名单配置 |
||||
|
/**
|
||||
|
* 这个数组中的属性名将会被忽略:不进行归档 |
||||
|
* |
||||
|
* @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 |
||||
|
*/ |
||||
|
+ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; |
||||
|
|
||||
|
/**
|
||||
|
* 这个数组中的属性名将会被忽略:不进行归档 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames; |
||||
|
|
||||
|
#pragma mark - 内部使用 |
||||
|
+ (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key; |
||||
|
@end |
@ -0,0 +1,174 @@ |
|||||
|
// |
||||
|
// NSObject+MJClass.m |
||||
|
// MJExtensionExample |
||||
|
// |
||||
|
// Created by MJ Lee on 15/8/11. |
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "NSObject+MJClass.h" |
||||
|
#import "NSObject+MJCoding.h" |
||||
|
#import "NSObject+MJKeyValue.h" |
||||
|
#import "MJFoundation.h" |
||||
|
#import <objc/runtime.h> |
||||
|
|
||||
|
static const char MJAllowedPropertyNamesKey = '\0'; |
||||
|
static const char MJIgnoredPropertyNamesKey = '\0'; |
||||
|
static const char MJAllowedCodingPropertyNamesKey = '\0'; |
||||
|
static const char MJIgnoredCodingPropertyNamesKey = '\0'; |
||||
|
|
||||
|
@implementation NSObject (MJClass) |
||||
|
|
||||
|
+ (NSMutableDictionary *)mj_classDictForKey:(const void *)key |
||||
|
{ |
||||
|
static NSMutableDictionary *allowedPropertyNamesDict; |
||||
|
static NSMutableDictionary *ignoredPropertyNamesDict; |
||||
|
static NSMutableDictionary *allowedCodingPropertyNamesDict; |
||||
|
static NSMutableDictionary *ignoredCodingPropertyNamesDict; |
||||
|
|
||||
|
static dispatch_once_t onceToken; |
||||
|
dispatch_once(&onceToken, ^{ |
||||
|
allowedPropertyNamesDict = [NSMutableDictionary dictionary]; |
||||
|
ignoredPropertyNamesDict = [NSMutableDictionary dictionary]; |
||||
|
allowedCodingPropertyNamesDict = [NSMutableDictionary dictionary]; |
||||
|
ignoredCodingPropertyNamesDict = [NSMutableDictionary dictionary]; |
||||
|
}); |
||||
|
|
||||
|
if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict; |
||||
|
if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict; |
||||
|
if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict; |
||||
|
if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict; |
||||
|
return nil; |
||||
|
} |
||||
|
|
||||
|
+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration |
||||
|
{ |
||||
|
// 1.没有block就直接返回 |
||||
|
if (enumeration == nil) return; |
||||
|
|
||||
|
// 2.停止遍历的标记 |
||||
|
BOOL stop = NO; |
||||
|
|
||||
|
// 3.当前正在遍历的类 |
||||
|
Class c = self; |
||||
|
|
||||
|
// 4.开始遍历每一个类 |
||||
|
while (c && !stop) { |
||||
|
// 4.1.执行操作 |
||||
|
enumeration(c, &stop); |
||||
|
|
||||
|
// 4.2.获得父类 |
||||
|
c = class_getSuperclass(c); |
||||
|
|
||||
|
if ([MJFoundation isClassFromFoundation:c]) break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
+ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration |
||||
|
{ |
||||
|
// 1.没有block就直接返回 |
||||
|
if (enumeration == nil) return; |
||||
|
|
||||
|
// 2.停止遍历的标记 |
||||
|
BOOL stop = NO; |
||||
|
|
||||
|
// 3.当前正在遍历的类 |
||||
|
Class c = self; |
||||
|
|
||||
|
// 4.开始遍历每一个类 |
||||
|
while (c && !stop) { |
||||
|
// 4.1.执行操作 |
||||
|
enumeration(c, &stop); |
||||
|
|
||||
|
// 4.2.获得父类 |
||||
|
c = class_getSuperclass(c); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 属性黑名单配置 |
||||
|
+ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames |
||||
|
{ |
||||
|
[self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_totalIgnoredPropertyNames |
||||
|
{ |
||||
|
return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 归档属性黑名单配置 |
||||
|
+ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames |
||||
|
{ |
||||
|
[self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames |
||||
|
{ |
||||
|
return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 属性白名单配置 |
||||
|
+ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; |
||||
|
{ |
||||
|
[self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_totalAllowedPropertyNames |
||||
|
{ |
||||
|
return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 归档属性白名单配置 |
||||
|
+ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames |
||||
|
{ |
||||
|
[self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_totalAllowedCodingPropertyNames |
||||
|
{ |
||||
|
return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - block和方法处理:存储block的返回值 |
||||
|
+ (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key { |
||||
|
MJExtensionSemaphoreCreate |
||||
|
MJ_LOCK(mje_signalSemaphore); |
||||
|
if (block) { |
||||
|
objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
||||
|
} else { |
||||
|
objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
||||
|
} |
||||
|
|
||||
|
// 清空数据 |
||||
|
[[self mj_classDictForKey:key] removeAllObjects]; |
||||
|
MJ_UNLOCK(mje_signalSemaphore); |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key |
||||
|
{ |
||||
|
MJExtensionSemaphoreCreate |
||||
|
MJ_LOCK(mje_signalSemaphore); |
||||
|
NSMutableArray *array = [self mj_classDictForKey:key][NSStringFromClass(self)]; |
||||
|
if (array == nil) { |
||||
|
// 创建、存储 |
||||
|
[self mj_classDictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array]; |
||||
|
|
||||
|
if ([self respondsToSelector:selector]) { |
||||
|
#pragma clang diagnostic push |
||||
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" |
||||
|
NSArray *subArray = [self performSelector:selector]; |
||||
|
#pragma clang diagnostic pop |
||||
|
if (subArray) { |
||||
|
[array addObjectsFromArray:subArray]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { |
||||
|
NSArray *subArray = objc_getAssociatedObject(c, key); |
||||
|
[array addObjectsFromArray:subArray]; |
||||
|
}]; |
||||
|
} |
||||
|
MJ_UNLOCK(mje_signalSemaphore); |
||||
|
return array; |
||||
|
} |
||||
|
@end |
@ -0,0 +1,66 @@ |
|||||
|
//
|
||||
|
// NSObject+MJCoding.h
|
||||
|
// MJExtension
|
||||
|
//
|
||||
|
// Created by mj on 14-1-15.
|
||||
|
// Copyright (c) 2014年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "MJExtensionConst.h" |
||||
|
|
||||
|
/**
|
||||
|
* Codeing协议 |
||||
|
*/ |
||||
|
@protocol MJCoding <NSObject> |
||||
|
@optional |
||||
|
/**
|
||||
|
* 这个数组中的属性名才会进行归档 |
||||
|
*/ |
||||
|
+ (NSArray *)mj_allowedCodingPropertyNames; |
||||
|
/**
|
||||
|
* 这个数组中的属性名将会被忽略:不进行归档 |
||||
|
*/ |
||||
|
+ (NSArray *)mj_ignoredCodingPropertyNames; |
||||
|
@end |
||||
|
|
||||
|
@interface NSObject (MJCoding) <MJCoding> |
||||
|
/**
|
||||
|
* 解码(从文件中解析对象) |
||||
|
*/ |
||||
|
- (void)mj_decode:(NSCoder *)decoder; |
||||
|
/**
|
||||
|
* 编码(将对象写入文件中) |
||||
|
*/ |
||||
|
- (void)mj_encode:(NSCoder *)encoder; |
||||
|
@end |
||||
|
|
||||
|
/**
|
||||
|
归档的实现 |
||||
|
*/ |
||||
|
#define MJCodingImplementation \ |
||||
|
- (id)initWithCoder:(NSCoder *)decoder \ |
||||
|
{ \ |
||||
|
if (self = [super init]) { \ |
||||
|
[self mj_decode:decoder]; \ |
||||
|
} \ |
||||
|
return self; \ |
||||
|
} \ |
||||
|
\ |
||||
|
- (void)encodeWithCoder:(NSCoder *)encoder \ |
||||
|
{ \ |
||||
|
[self mj_encode:encoder]; \ |
||||
|
}\ |
||||
|
|
||||
|
#define MJExtensionCodingImplementation MJCodingImplementation |
||||
|
|
||||
|
#define MJSecureCodingImplementation(CLASS, FLAG) \ |
||||
|
@interface CLASS (MJSecureCoding) <NSSecureCoding> \ |
||||
|
@end \ |
||||
|
@implementation CLASS (MJSecureCoding) \ |
||||
|
MJCodingImplementation \ |
||||
|
+ (BOOL)supportsSecureCoding { \ |
||||
|
return FLAG; \ |
||||
|
} \ |
||||
|
@end \ |
||||
|
|
@ -0,0 +1,59 @@ |
|||||
|
// |
||||
|
// NSObject+MJCoding.m |
||||
|
// MJExtension |
||||
|
// |
||||
|
// Created by mj on 14-1-15. |
||||
|
// Copyright (c) 2014年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "NSObject+MJCoding.h" |
||||
|
#import "NSObject+MJClass.h" |
||||
|
#import "NSObject+MJProperty.h" |
||||
|
#import "MJProperty.h" |
||||
|
|
||||
|
@implementation NSObject (MJCoding) |
||||
|
|
||||
|
- (void)mj_encode:(NSCoder *)encoder |
||||
|
{ |
||||
|
Class clazz = [self class]; |
||||
|
|
||||
|
NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; |
||||
|
NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; |
||||
|
|
||||
|
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { |
||||
|
// 检测是否被忽略 |
||||
|
if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; |
||||
|
if ([ignoredCodingPropertyNames containsObject:property.name]) return; |
||||
|
|
||||
|
id value = [property valueForObject:self]; |
||||
|
if (value == nil) return; |
||||
|
[encoder encodeObject:value forKey:property.name]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)mj_decode:(NSCoder *)decoder |
||||
|
{ |
||||
|
Class clazz = [self class]; |
||||
|
|
||||
|
NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; |
||||
|
NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; |
||||
|
|
||||
|
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { |
||||
|
// 检测是否被忽略 |
||||
|
if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; |
||||
|
if ([ignoredCodingPropertyNames containsObject:property.name]) return; |
||||
|
|
||||
|
// fixed `-[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber'(This will be disallowed in the future.)` warning. |
||||
|
Class genericClass = [property objectClassInArrayForClass:property.srcClass]; |
||||
|
// If genericClass exists, property.type.typeClass would be a collection type(Array, Set, Dictionary). This scenario([obj, nil, obj, nil]) would not happened. |
||||
|
NSSet *classes = [NSSet setWithObjects:NSNumber.class, |
||||
|
property.type.typeClass, genericClass, nil]; |
||||
|
id value = [decoder decodeObjectOfClasses:classes forKey:property.name]; |
||||
|
if (value == nil) { // 兼容以前的MJExtension版本 |
||||
|
value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]]; |
||||
|
} |
||||
|
if (value == nil) return; |
||||
|
[property setValue:value forObject:self]; |
||||
|
}]; |
||||
|
} |
||||
|
@end |
@ -0,0 +1,194 @@ |
|||||
|
//
|
||||
|
// NSObject+MJKeyValue.h
|
||||
|
// MJExtension
|
||||
|
//
|
||||
|
// Created by mj on 13-8-24.
|
||||
|
// Copyright (c) 2013年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "MJExtensionConst.h" |
||||
|
#import <CoreData/CoreData.h> |
||||
|
#import "MJProperty.h" |
||||
|
|
||||
|
/**
|
||||
|
* KeyValue协议 |
||||
|
*/ |
||||
|
@protocol MJKeyValue <NSObject> |
||||
|
@optional |
||||
|
/**
|
||||
|
* 只有这个数组中的属性名才允许进行字典和模型的转换 |
||||
|
*/ |
||||
|
+ (NSArray *)mj_allowedPropertyNames; |
||||
|
|
||||
|
/**
|
||||
|
* 这个数组中的属性名将会被忽略:不进行字典和模型的转换 |
||||
|
*/ |
||||
|
+ (NSArray *)mj_ignoredPropertyNames; |
||||
|
|
||||
|
/**
|
||||
|
* 将属性名换为其他key去字典中取值 |
||||
|
* |
||||
|
* @return 字典中的key是属性名,value是从字典中取值用的key |
||||
|
*/ |
||||
|
+ (NSDictionary *)mj_replacedKeyFromPropertyName; |
||||
|
|
||||
|
/**
|
||||
|
* 将属性名换为其他key去字典中取值 |
||||
|
* |
||||
|
* @return 从字典中取值用的key |
||||
|
*/ |
||||
|
+ (id)mj_replacedKeyFromPropertyName121:(NSString *)propertyName; |
||||
|
|
||||
|
/**
|
||||
|
* 数组中需要转换的模型类 |
||||
|
* |
||||
|
* @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) |
||||
|
*/ |
||||
|
+ (NSDictionary *)mj_objectClassInArray; |
||||
|
|
||||
|
|
||||
|
/** 特殊地区在字符串格式化数字时使用 */ |
||||
|
+ (NSLocale *)mj_numberLocale; |
||||
|
|
||||
|
/**
|
||||
|
* 旧值换新值,用于过滤字典中的值 |
||||
|
* |
||||
|
* @param oldValue 旧值 |
||||
|
* |
||||
|
* @return 新值 |
||||
|
*/ |
||||
|
- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property; |
||||
|
|
||||
|
/**
|
||||
|
* 当字典转模型完毕时调用 |
||||
|
*/ |
||||
|
- (void)mj_keyValuesDidFinishConvertingToObject MJExtensionDeprecated("请使用`mj_didConvertToObjectWithKeyValues:`替代"); |
||||
|
- (void)mj_keyValuesDidFinishConvertingToObject:(NSDictionary *)keyValues MJExtensionDeprecated("请使用`mj_didConvertToObjectWithKeyValues:`替代"); |
||||
|
- (void)mj_didConvertToObjectWithKeyValues:(NSDictionary *)keyValues; |
||||
|
|
||||
|
/**
|
||||
|
* 当模型转字典完毕时调用 |
||||
|
*/ |
||||
|
- (void)mj_objectDidFinishConvertingToKeyValues MJExtensionDeprecated("请使用`mj_objectDidConvertToKeyValues:`替代"); |
||||
|
- (void)mj_objectDidConvertToKeyValues:(NSMutableDictionary *)keyValues; |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@interface NSObject (MJKeyValue) <MJKeyValue> |
||||
|
#pragma mark - 类方法 |
||||
|
/**
|
||||
|
* 字典转模型过程中遇到的错误 |
||||
|
*/ |
||||
|
+ (NSError *)mj_error; |
||||
|
|
||||
|
/**
|
||||
|
* 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) |
||||
|
*/ |
||||
|
+ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; |
||||
|
|
||||
|
#pragma mark - 对象方法 |
||||
|
/**
|
||||
|
* 将字典的键值对转成模型属性 |
||||
|
* @param keyValues 字典(可以是NSDictionary、NSData、NSString) |
||||
|
*/ |
||||
|
- (instancetype)mj_setKeyValues:(id)keyValues; |
||||
|
|
||||
|
/**
|
||||
|
* 将字典的键值对转成模型属性 |
||||
|
* @param keyValues 字典(可以是NSDictionary、NSData、NSString) |
||||
|
* @param context CoreData上下文 |
||||
|
*/ |
||||
|
- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; |
||||
|
|
||||
|
/**
|
||||
|
* 将模型转成字典 |
||||
|
* @return 字典 |
||||
|
*/ |
||||
|
- (NSMutableDictionary *)mj_keyValues; |
||||
|
- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys; |
||||
|
- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; |
||||
|
|
||||
|
/**
|
||||
|
* 通过模型数组来创建一个字典数组 |
||||
|
* @param objectArray 模型数组 |
||||
|
* @return 字典数组 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray; |
||||
|
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; |
||||
|
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; |
||||
|
|
||||
|
#pragma mark - 字典转模型 |
||||
|
/**
|
||||
|
* 通过字典来创建一个模型 |
||||
|
* @param keyValues 字典(可以是NSDictionary、NSData、NSString) |
||||
|
* @return 新建的对象 |
||||
|
*/ |
||||
|
+ (instancetype)mj_objectWithKeyValues:(id)keyValues; |
||||
|
|
||||
|
/**
|
||||
|
* 通过字典来创建一个CoreData模型 |
||||
|
* @param keyValues 字典(可以是NSDictionary、NSData、NSString) |
||||
|
* @param context CoreData上下文 |
||||
|
* @return 新建的对象 |
||||
|
*/ |
||||
|
+ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; |
||||
|
|
||||
|
/**
|
||||
|
* 通过plist来创建一个模型 |
||||
|
* @param filename 文件名(仅限于mainBundle中的文件) |
||||
|
* @return 新建的对象 |
||||
|
*/ |
||||
|
+ (instancetype)mj_objectWithFilename:(NSString *)filename; |
||||
|
|
||||
|
/**
|
||||
|
* 通过plist来创建一个模型 |
||||
|
* @param file 文件全路径 |
||||
|
* @return 新建的对象 |
||||
|
*/ |
||||
|
+ (instancetype)mj_objectWithFile:(NSString *)file; |
||||
|
|
||||
|
#pragma mark - 字典数组转模型数组 |
||||
|
/**
|
||||
|
* 通过字典数组来创建一个模型数组 |
||||
|
* @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) |
||||
|
* @return 模型数组 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray; |
||||
|
|
||||
|
/**
|
||||
|
* 通过字典数组来创建一个模型数组 |
||||
|
* @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) |
||||
|
* @param context CoreData上下文 |
||||
|
* @return 模型数组 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; |
||||
|
|
||||
|
/**
|
||||
|
* 通过plist来创建一个模型数组 |
||||
|
* @param filename 文件名(仅限于mainBundle中的文件) |
||||
|
* @return 模型数组 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename; |
||||
|
|
||||
|
/**
|
||||
|
* 通过plist来创建一个模型数组 |
||||
|
* @param file 文件全路径 |
||||
|
* @return 模型数组 |
||||
|
*/ |
||||
|
+ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file; |
||||
|
|
||||
|
#pragma mark - 转换为JSON |
||||
|
/**
|
||||
|
* 转换为JSON Data |
||||
|
*/ |
||||
|
- (NSData *)mj_JSONData; |
||||
|
/**
|
||||
|
* 转换为字典或者数组 |
||||
|
*/ |
||||
|
- (id)mj_JSONObject; |
||||
|
/**
|
||||
|
* 转换为JSON 字符串 |
||||
|
*/ |
||||
|
- (NSString *)mj_JSONString; |
||||
|
@end |
@ -0,0 +1,524 @@ |
|||||
|
// |
||||
|
// NSObject+MJKeyValue.m |
||||
|
// MJExtension |
||||
|
// |
||||
|
// Created by mj on 13-8-24. |
||||
|
// Copyright (c) 2013年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "NSObject+MJKeyValue.h" |
||||
|
#import "NSObject+MJProperty.h" |
||||
|
#import "NSString+MJExtension.h" |
||||
|
#import "MJProperty.h" |
||||
|
#import "MJPropertyType.h" |
||||
|
#import "MJExtensionConst.h" |
||||
|
#import "MJFoundation.h" |
||||
|
#import "NSString+MJExtension.h" |
||||
|
#import "NSObject+MJClass.h" |
||||
|
|
||||
|
@implementation NSDecimalNumber(MJKeyValue) |
||||
|
|
||||
|
- (id)mj_standardValueWithTypeCode:(NSString *)typeCode { |
||||
|
// 由于这里涉及到编译器问题, 暂时保留 Long, 实际上在 64 位系统上, 这 2 个精度范围相同, |
||||
|
// 32 位略有不同, 其余都可使用 Double 进行强转不丢失精度 |
||||
|
if ([typeCode isEqualToString:MJPropertyTypeLongLong]) { |
||||
|
return @(self.longLongValue); |
||||
|
} else if ([typeCode isEqualToString:MJPropertyTypeLongLong.uppercaseString]) { |
||||
|
return @(self.unsignedLongLongValue); |
||||
|
} else if ([typeCode isEqualToString:MJPropertyTypeLong]) { |
||||
|
return @(self.longValue); |
||||
|
} else if ([typeCode isEqualToString:MJPropertyTypeLong.uppercaseString]) { |
||||
|
return @(self.unsignedLongValue); |
||||
|
} else { |
||||
|
return @(self.doubleValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@implementation NSObject (MJKeyValue) |
||||
|
|
||||
|
#pragma mark - 错误 |
||||
|
static const char MJErrorKey = '\0'; |
||||
|
+ (NSError *)mj_error |
||||
|
{ |
||||
|
return objc_getAssociatedObject(self, &MJErrorKey); |
||||
|
} |
||||
|
|
||||
|
+ (void)setMj_error:(NSError *)error |
||||
|
{ |
||||
|
objc_setAssociatedObject(self, &MJErrorKey, error, OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 模型 -> 字典时的参考 |
||||
|
/** 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) */ |
||||
|
static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0'; |
||||
|
|
||||
|
+ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference |
||||
|
{ |
||||
|
objc_setAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey, @(reference), OBJC_ASSOCIATION_ASSIGN); |
||||
|
} |
||||
|
|
||||
|
+ (BOOL)mj_isReferenceReplacedKeyWhenCreatingKeyValues |
||||
|
{ |
||||
|
__block id value = objc_getAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); |
||||
|
if (!value) { |
||||
|
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { |
||||
|
value = objc_getAssociatedObject(c, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); |
||||
|
|
||||
|
if (value) *stop = YES; |
||||
|
}]; |
||||
|
} |
||||
|
return [value boolValue]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - --常用的对象-- |
||||
|
+ (void)load |
||||
|
{ |
||||
|
// 默认设置 |
||||
|
[self mj_referenceReplacedKeyWhenCreatingKeyValues:YES]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - --公共方法-- |
||||
|
#pragma mark - 字典 -> 模型 |
||||
|
- (instancetype)mj_setKeyValues:(id)keyValues |
||||
|
{ |
||||
|
return [self mj_setKeyValues:keyValues context:nil]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
核心代码: |
||||
|
*/ |
||||
|
- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context |
||||
|
{ |
||||
|
// 获得JSON对象 |
||||
|
keyValues = [keyValues mj_JSONObject]; |
||||
|
|
||||
|
MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues参数不是一个字典"); |
||||
|
|
||||
|
Class clazz = [self class]; |
||||
|
NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; |
||||
|
NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; |
||||
|
|
||||
|
NSLocale *numberLocale = nil; |
||||
|
if ([self.class respondsToSelector:@selector(mj_numberLocale)]) { |
||||
|
numberLocale = self.class.mj_numberLocale; |
||||
|
} |
||||
|
|
||||
|
//通过封装的方法回调一个通过运行时编写的,用于返回属性列表的方法。 |
||||
|
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { |
||||
|
@try { |
||||
|
// 0.检测是否被忽略 |
||||
|
if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; |
||||
|
if ([ignoredPropertyNames containsObject:property.name]) return; |
||||
|
|
||||
|
// 1.取出属性值 |
||||
|
id value; |
||||
|
NSArray *propertyKeyses = [property propertyKeysForClass:clazz]; |
||||
|
for (NSArray *propertyKeys in propertyKeyses) { |
||||
|
value = keyValues; |
||||
|
for (MJPropertyKey *propertyKey in propertyKeys) { |
||||
|
value = [propertyKey valueInObject:value]; |
||||
|
} |
||||
|
if (value) break; |
||||
|
} |
||||
|
|
||||
|
// 值的过滤 |
||||
|
id newValue = [clazz mj_getNewValueFromObject:self oldValue:value property:property]; |
||||
|
if (newValue != value) { // 有过滤后的新值 |
||||
|
[property setValue:newValue forObject:self]; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 如果没有值,就直接返回 |
||||
|
if (!value || value == [NSNull null]) return; |
||||
|
|
||||
|
// 2.复杂处理 |
||||
|
MJPropertyType *type = property.type; |
||||
|
Class propertyClass = type.typeClass; |
||||
|
Class objectClass = [property objectClassInArrayForClass:[self class]]; |
||||
|
|
||||
|
// 不可变 -> 可变处理 |
||||
|
if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) { |
||||
|
value = [NSMutableArray arrayWithArray:value]; |
||||
|
} else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) { |
||||
|
value = [NSMutableDictionary dictionaryWithDictionary:value]; |
||||
|
} else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) { |
||||
|
value = [NSMutableString stringWithString:value]; |
||||
|
} else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) { |
||||
|
value = [NSMutableData dataWithData:value]; |
||||
|
} |
||||
|
|
||||
|
if (!type.isFromFoundation && propertyClass) { // 模型属性 |
||||
|
value = [propertyClass mj_objectWithKeyValues:value context:context]; |
||||
|
} else if (objectClass) { |
||||
|
if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) { |
||||
|
// string array -> url array |
||||
|
NSMutableArray *urlArray = [NSMutableArray array]; |
||||
|
for (NSString *string in value) { |
||||
|
if (![string isKindOfClass:[NSString class]]) continue; |
||||
|
[urlArray addObject:string.mj_url]; |
||||
|
} |
||||
|
value = urlArray; |
||||
|
} else { // 字典数组-->模型数组 |
||||
|
value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context]; |
||||
|
} |
||||
|
} else if (propertyClass == [NSString class]) { |
||||
|
if ([value isKindOfClass:[NSNumber class]]) { |
||||
|
// NSNumber -> NSString |
||||
|
value = [value description]; |
||||
|
} else if ([value isKindOfClass:[NSURL class]]) { |
||||
|
// NSURL -> NSString |
||||
|
value = [value absoluteString]; |
||||
|
} |
||||
|
} else if ([value isKindOfClass:[NSString class]]) { |
||||
|
if (propertyClass == [NSURL class]) { |
||||
|
// NSString -> NSURL |
||||
|
// 字符串转码 |
||||
|
value = [value mj_url]; |
||||
|
} else if (type.isNumberType) { |
||||
|
NSString *oldValue = value; |
||||
|
|
||||
|
// NSString -> NSDecimalNumber, 使用 DecimalNumber 来转换数字, 避免丢失精度以及溢出 |
||||
|
NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString:oldValue |
||||
|
locale:numberLocale]; |
||||
|
|
||||
|
// 检查特殊情况 |
||||
|
if (decimalValue == NSDecimalNumber.notANumber) { |
||||
|
value = @(0); |
||||
|
}else if (propertyClass != [NSDecimalNumber class]) { |
||||
|
value = [decimalValue mj_standardValueWithTypeCode:type.code]; |
||||
|
} else { |
||||
|
value = decimalValue; |
||||
|
} |
||||
|
|
||||
|
// 如果是BOOL |
||||
|
if (type.isBoolType) { |
||||
|
// 字符串转BOOL(字符串没有charValue方法) |
||||
|
// 系统会调用字符串的charValue转为BOOL类型 |
||||
|
NSString *lower = [oldValue lowercaseString]; |
||||
|
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) { |
||||
|
value = @YES; |
||||
|
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) { |
||||
|
value = @NO; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){ |
||||
|
// 过滤 NSDecimalNumber类型 |
||||
|
if (![value isKindOfClass:[NSDecimalNumber class]]) { |
||||
|
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 经过转换后, 最终检查 value 与 property 是否匹配 |
||||
|
if (propertyClass && ![value isKindOfClass:propertyClass]) { |
||||
|
value = nil; |
||||
|
} |
||||
|
|
||||
|
// 3.赋值 |
||||
|
[property setValue:value forObject:self]; |
||||
|
} @catch (NSException *exception) { |
||||
|
MJExtensionBuildError([self class], exception.reason); |
||||
|
MJExtensionLog(@"%@", exception); |
||||
|
#ifdef DEBUG |
||||
|
[exception raise]; |
||||
|
#endif |
||||
|
} |
||||
|
}]; |
||||
|
|
||||
|
// 转换完毕 |
||||
|
if ([self respondsToSelector:@selector(mj_didConvertToObjectWithKeyValues:)]) { |
||||
|
[self mj_didConvertToObjectWithKeyValues:keyValues]; |
||||
|
} |
||||
|
#pragma clang diagnostic push |
||||
|
#pragma clang diagnostic ignored"-Wdeprecated-declarations" |
||||
|
if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject)]) { |
||||
|
[self mj_keyValuesDidFinishConvertingToObject]; |
||||
|
} |
||||
|
if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject:)]) { |
||||
|
[self mj_keyValuesDidFinishConvertingToObject:keyValues]; |
||||
|
} |
||||
|
#pragma clang diagnostic pop |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
+ (instancetype)mj_objectWithKeyValues:(id)keyValues |
||||
|
{ |
||||
|
return [self mj_objectWithKeyValues:keyValues context:nil]; |
||||
|
} |
||||
|
|
||||
|
+ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context |
||||
|
{ |
||||
|
// 获得JSON对象 |
||||
|
keyValues = [keyValues mj_JSONObject]; |
||||
|
MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues参数不是一个字典"); |
||||
|
|
||||
|
if ([self isSubclassOfClass:[NSManagedObject class]] && context) { |
||||
|
NSString *entityName = [(NSManagedObject *)self entity].name; |
||||
|
return [[NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context] mj_setKeyValues:keyValues context:context]; |
||||
|
} |
||||
|
return [[[self alloc] init] mj_setKeyValues:keyValues]; |
||||
|
} |
||||
|
|
||||
|
+ (instancetype)mj_objectWithFilename:(NSString *)filename |
||||
|
{ |
||||
|
MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); |
||||
|
|
||||
|
return [self mj_objectWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; |
||||
|
} |
||||
|
|
||||
|
+ (instancetype)mj_objectWithFile:(NSString *)file |
||||
|
{ |
||||
|
MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); |
||||
|
|
||||
|
return [self mj_objectWithKeyValues:[NSDictionary dictionaryWithContentsOfFile:file]]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 字典数组 -> 模型数组 |
||||
|
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray |
||||
|
{ |
||||
|
return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:nil]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context |
||||
|
{ |
||||
|
// 如果是JSON字符串 |
||||
|
keyValuesArray = [keyValuesArray mj_JSONObject]; |
||||
|
|
||||
|
// 1.判断真实性 |
||||
|
MJExtensionAssertError([keyValuesArray isKindOfClass:[NSArray class]], nil, [self class], @"keyValuesArray参数不是一个数组"); |
||||
|
|
||||
|
// 如果数组里面放的是NSString、NSNumber等数据 |
||||
|
if ([MJFoundation isClassFromFoundation:self]) return [NSMutableArray arrayWithArray:keyValuesArray]; |
||||
|
|
||||
|
|
||||
|
// 2.创建数组 |
||||
|
NSMutableArray *modelArray = [NSMutableArray array]; |
||||
|
|
||||
|
// 3.遍历 |
||||
|
for (NSDictionary *keyValues in keyValuesArray) { |
||||
|
if ([keyValues isKindOfClass:[NSArray class]]){ |
||||
|
[modelArray addObject:[self mj_objectArrayWithKeyValuesArray:keyValues context:context]]; |
||||
|
} else { |
||||
|
id model = [self mj_objectWithKeyValues:keyValues context:context]; |
||||
|
if (model) [modelArray addObject:model]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return modelArray; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename |
||||
|
{ |
||||
|
MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); |
||||
|
|
||||
|
return [self mj_objectArrayWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file |
||||
|
{ |
||||
|
MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); |
||||
|
|
||||
|
return [self mj_objectArrayWithKeyValuesArray:[NSArray arrayWithContentsOfFile:file]]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 模型 -> 字典 |
||||
|
- (NSMutableDictionary *)mj_keyValues |
||||
|
{ |
||||
|
return [self mj_keyValuesWithKeys:nil ignoredKeys:nil]; |
||||
|
} |
||||
|
|
||||
|
- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys |
||||
|
{ |
||||
|
return [self mj_keyValuesWithKeys:keys ignoredKeys:nil]; |
||||
|
} |
||||
|
|
||||
|
- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys |
||||
|
{ |
||||
|
return [self mj_keyValuesWithKeys:nil ignoredKeys:ignoredKeys]; |
||||
|
} |
||||
|
|
||||
|
- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys |
||||
|
{ |
||||
|
// 如果自己不是模型类, 那就返回自己 |
||||
|
// 模型类过滤掉 NSNull |
||||
|
// 唯一一个不返回自己的 |
||||
|
if ([self isMemberOfClass:NSNull.class]) { return nil; } |
||||
|
// 这里虽然返回了自己, 但是其实是有报错信息的. |
||||
|
// TODO: 报错机制不好, 需要重做 |
||||
|
MJExtensionAssertError(![MJFoundation isClassFromFoundation:[self class]], (NSMutableDictionary *)self, [self class], @"不是自定义的模型类") |
||||
|
|
||||
|
id keyValues = [NSMutableDictionary dictionary]; |
||||
|
|
||||
|
Class clazz = [self class]; |
||||
|
NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; |
||||
|
NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; |
||||
|
|
||||
|
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { |
||||
|
@try { |
||||
|
// 0.检测是否被忽略 |
||||
|
if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; |
||||
|
if ([ignoredPropertyNames containsObject:property.name]) return; |
||||
|
if (keys.count && ![keys containsObject:property.name]) return; |
||||
|
if ([ignoredKeys containsObject:property.name]) return; |
||||
|
|
||||
|
// 1.取出属性值 |
||||
|
id value = [property valueForObject:self]; |
||||
|
if (!value) return; |
||||
|
|
||||
|
// 2.如果是模型属性 |
||||
|
MJPropertyType *type = property.type; |
||||
|
Class propertyClass = type.typeClass; |
||||
|
if (!type.isFromFoundation && propertyClass) { |
||||
|
value = [value mj_keyValues]; |
||||
|
} else if ([value isKindOfClass:[NSArray class]]) { |
||||
|
// 3.处理数组里面有模型的情况 |
||||
|
value = [NSObject mj_keyValuesArrayWithObjectArray:value]; |
||||
|
} else if (propertyClass == [NSURL class]) { |
||||
|
value = [value absoluteString]; |
||||
|
} |
||||
|
|
||||
|
// 4.赋值 |
||||
|
if ([clazz mj_isReferenceReplacedKeyWhenCreatingKeyValues]) { |
||||
|
NSArray *propertyKeys = [[property propertyKeysForClass:clazz] firstObject]; |
||||
|
NSUInteger keyCount = propertyKeys.count; |
||||
|
// 创建字典 |
||||
|
__block id innerContainer = keyValues; |
||||
|
[propertyKeys enumerateObjectsUsingBlock:^(MJPropertyKey *propertyKey, NSUInteger idx, BOOL *stop) { |
||||
|
// 下一个属性 |
||||
|
MJPropertyKey *nextPropertyKey = nil; |
||||
|
if (idx != keyCount - 1) { |
||||
|
nextPropertyKey = propertyKeys[idx + 1]; |
||||
|
} |
||||
|
|
||||
|
if (nextPropertyKey) { // 不是最后一个key |
||||
|
// 当前propertyKey对应的字典或者数组 |
||||
|
id tempInnerContainer = [propertyKey valueInObject:innerContainer]; |
||||
|
if (tempInnerContainer == nil || [tempInnerContainer isKindOfClass:[NSNull class]]) { |
||||
|
if (nextPropertyKey.type == MJPropertyKeyTypeDictionary) { |
||||
|
tempInnerContainer = [NSMutableDictionary dictionary]; |
||||
|
} else { |
||||
|
tempInnerContainer = [NSMutableArray array]; |
||||
|
} |
||||
|
if (propertyKey.type == MJPropertyKeyTypeDictionary) { |
||||
|
innerContainer[propertyKey.name] = tempInnerContainer; |
||||
|
} else { |
||||
|
innerContainer[propertyKey.name.intValue] = tempInnerContainer; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ([tempInnerContainer isKindOfClass:[NSMutableArray class]]) { |
||||
|
NSMutableArray *tempInnerContainerArray = tempInnerContainer; |
||||
|
int index = nextPropertyKey.name.intValue; |
||||
|
while (tempInnerContainerArray.count < index + 1) { |
||||
|
[tempInnerContainerArray addObject:[NSNull null]]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
innerContainer = tempInnerContainer; |
||||
|
} else { // 最后一个key |
||||
|
if (propertyKey.type == MJPropertyKeyTypeDictionary) { |
||||
|
innerContainer[propertyKey.name] = value; |
||||
|
} else { |
||||
|
innerContainer[propertyKey.name.intValue] = value; |
||||
|
} |
||||
|
} |
||||
|
}]; |
||||
|
} else { |
||||
|
keyValues[property.name] = value; |
||||
|
} |
||||
|
} @catch (NSException *exception) { |
||||
|
MJExtensionBuildError([self class], exception.reason); |
||||
|
MJExtensionLog(@"%@", exception); |
||||
|
#ifdef DEBUG |
||||
|
[exception raise]; |
||||
|
#endif |
||||
|
} |
||||
|
}]; |
||||
|
|
||||
|
// 转换完毕 |
||||
|
if ([self respondsToSelector:@selector(mj_objectDidConvertToKeyValues:)]) { |
||||
|
[self mj_objectDidConvertToKeyValues:keyValues]; |
||||
|
} |
||||
|
#pragma clang diagnostic push |
||||
|
#pragma clang diagnostic ignored"-Wdeprecated-declarations" |
||||
|
if ([self respondsToSelector:@selector(mj_objectDidFinishConvertingToKeyValues)]) { |
||||
|
[self mj_objectDidFinishConvertingToKeyValues]; |
||||
|
} |
||||
|
#pragma clang diagnostic pop |
||||
|
|
||||
|
return keyValues; |
||||
|
} |
||||
|
#pragma mark - 模型数组 -> 字典数组 |
||||
|
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray |
||||
|
{ |
||||
|
return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:nil]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys |
||||
|
{ |
||||
|
return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys ignoredKeys:nil]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys |
||||
|
{ |
||||
|
return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:ignoredKeys]; |
||||
|
} |
||||
|
|
||||
|
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys |
||||
|
{ |
||||
|
// 0.判断真实性 |
||||
|
MJExtensionAssertError([objectArray isKindOfClass:[NSArray class]], nil, [self class], @"objectArray参数不是一个数组"); |
||||
|
|
||||
|
// 1.创建数组 |
||||
|
NSMutableArray *keyValuesArray = [NSMutableArray array]; |
||||
|
for (id object in objectArray) { |
||||
|
if (keys) { |
||||
|
id convertedObj = [object mj_keyValuesWithKeys:keys]; |
||||
|
if (!convertedObj) { continue; } |
||||
|
[keyValuesArray addObject:convertedObj]; |
||||
|
} else { |
||||
|
id convertedObj = [object mj_keyValuesWithIgnoredKeys:ignoredKeys]; |
||||
|
if (!convertedObj) { continue; } |
||||
|
[keyValuesArray addObject:convertedObj]; |
||||
|
} |
||||
|
} |
||||
|
return keyValuesArray; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 转换为JSON |
||||
|
- (NSData *)mj_JSONData |
||||
|
{ |
||||
|
if ([self isKindOfClass:[NSString class]]) { |
||||
|
return [((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding]; |
||||
|
} else if ([self isKindOfClass:[NSData class]]) { |
||||
|
return (NSData *)self; |
||||
|
} |
||||
|
|
||||
|
return [NSJSONSerialization dataWithJSONObject:[self mj_JSONObject] options:kNilOptions error:nil]; |
||||
|
} |
||||
|
|
||||
|
- (id)mj_JSONObject |
||||
|
{ |
||||
|
if ([self isKindOfClass:[NSString class]]) { |
||||
|
return [NSJSONSerialization JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil]; |
||||
|
} else if ([self isKindOfClass:[NSData class]]) { |
||||
|
return [NSJSONSerialization JSONObjectWithData:(NSData *)self options:kNilOptions error:nil]; |
||||
|
} |
||||
|
|
||||
|
return self.mj_keyValues; |
||||
|
} |
||||
|
|
||||
|
- (NSString *)mj_JSONString |
||||
|
{ |
||||
|
if ([self isKindOfClass:[NSString class]]) { |
||||
|
return (NSString *)self; |
||||
|
} else if ([self isKindOfClass:[NSData class]]) { |
||||
|
return [[NSString alloc] initWithData:(NSData *)self encoding:NSUTF8StringEncoding]; |
||||
|
} |
||||
|
|
||||
|
return [[NSString alloc] initWithData:[self mj_JSONData] encoding:NSUTF8StringEncoding]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,70 @@ |
|||||
|
//
|
||||
|
// NSObject+MJProperty.h
|
||||
|
// MJExtensionExample
|
||||
|
//
|
||||
|
// Created by MJ Lee on 15/4/17.
|
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "MJExtensionConst.h" |
||||
|
|
||||
|
@class MJProperty; |
||||
|
|
||||
|
/**
|
||||
|
* 遍历成员变量用的block |
||||
|
* |
||||
|
* @param property 成员的包装对象 |
||||
|
* @param stop YES代表停止遍历,NO代表继续遍历 |
||||
|
*/ |
||||
|
typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); |
||||
|
|
||||
|
/** 将属性名换为其他key去字典中取值 */ |
||||
|
typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(void); |
||||
|
typedef id (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); |
||||
|
/** 数组中需要转换的模型类 */ |
||||
|
typedef NSDictionary * (^MJObjectClassInArray)(void); |
||||
|
/** 用于过滤字典中的值 */ |
||||
|
typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); |
||||
|
|
||||
|
/**
|
||||
|
* 成员属性相关的扩展 |
||||
|
*/ |
||||
|
@interface NSObject (MJProperty) |
||||
|
#pragma mark - 遍历 |
||||
|
/**
|
||||
|
* 遍历所有的成员 |
||||
|
*/ |
||||
|
+ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration; |
||||
|
|
||||
|
#pragma mark - 新值配置 |
||||
|
/**
|
||||
|
* 用于过滤字典中的值 |
||||
|
* |
||||
|
* @param newValueFormOldValue 用于过滤字典中的值 |
||||
|
*/ |
||||
|
+ (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; |
||||
|
+ (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property; |
||||
|
|
||||
|
#pragma mark - key配置 |
||||
|
/**
|
||||
|
* 将属性名换为其他key去字典中取值 |
||||
|
* |
||||
|
* @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 |
||||
|
*/ |
||||
|
+ (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; |
||||
|
/**
|
||||
|
* 将属性名换为其他key去字典中取值 |
||||
|
* |
||||
|
* @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 |
||||
|
*/ |
||||
|
+ (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; |
||||
|
|
||||
|
#pragma mark - array model class配置 |
||||
|
/**
|
||||
|
* 数组中需要转换的模型类 |
||||
|
* |
||||
|
* @param objectClassInArray 数组中需要转换的模型类 |
||||
|
*/ |
||||
|
+ (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; |
||||
|
@end |
@ -0,0 +1,240 @@ |
|||||
|
// |
||||
|
// NSObject+MJProperty.m |
||||
|
// MJExtensionExample |
||||
|
// |
||||
|
// Created by MJ Lee on 15/4/17. |
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "NSObject+MJProperty.h" |
||||
|
#import "NSObject+MJKeyValue.h" |
||||
|
#import "NSObject+MJCoding.h" |
||||
|
#import "NSObject+MJClass.h" |
||||
|
#import "MJProperty.h" |
||||
|
#import "MJFoundation.h" |
||||
|
#import <objc/runtime.h> |
||||
|
|
||||
|
#pragma clang diagnostic push |
||||
|
#pragma clang diagnostic ignored "-Wundeclared-selector" |
||||
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" |
||||
|
|
||||
|
static const char MJReplacedKeyFromPropertyNameKey = '\0'; |
||||
|
static const char MJReplacedKeyFromPropertyName121Key = '\0'; |
||||
|
static const char MJNewValueFromOldValueKey = '\0'; |
||||
|
static const char MJObjectClassInArrayKey = '\0'; |
||||
|
|
||||
|
static const char MJCachedPropertiesKey = '\0'; |
||||
|
|
||||
|
dispatch_semaphore_t mje_signalSemaphore; |
||||
|
dispatch_once_t mje_onceTokenSemaphore; |
||||
|
|
||||
|
@implementation NSObject (Property) |
||||
|
|
||||
|
+ (NSMutableDictionary *)mj_propertyDictForKey:(const void *)key |
||||
|
{ |
||||
|
static NSMutableDictionary *replacedKeyFromPropertyNameDict; |
||||
|
static NSMutableDictionary *replacedKeyFromPropertyName121Dict; |
||||
|
static NSMutableDictionary *newValueFromOldValueDict; |
||||
|
static NSMutableDictionary *objectClassInArrayDict; |
||||
|
static NSMutableDictionary *cachedPropertiesDict; |
||||
|
|
||||
|
static dispatch_once_t onceToken; |
||||
|
dispatch_once(&onceToken, ^{ |
||||
|
replacedKeyFromPropertyNameDict = [NSMutableDictionary dictionary]; |
||||
|
replacedKeyFromPropertyName121Dict = [NSMutableDictionary dictionary]; |
||||
|
newValueFromOldValueDict = [NSMutableDictionary dictionary]; |
||||
|
objectClassInArrayDict = [NSMutableDictionary dictionary]; |
||||
|
cachedPropertiesDict = [NSMutableDictionary dictionary]; |
||||
|
}); |
||||
|
|
||||
|
if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict; |
||||
|
if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict; |
||||
|
if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict; |
||||
|
if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict; |
||||
|
if (key == &MJCachedPropertiesKey) return cachedPropertiesDict; |
||||
|
return nil; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - --私有方法-- |
||||
|
+ (id)mj_propertyKey:(NSString *)propertyName |
||||
|
{ |
||||
|
MJExtensionAssertParamNotNil2(propertyName, nil); |
||||
|
|
||||
|
__block id key = nil; |
||||
|
// 查看有没有需要替换的key |
||||
|
if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) { |
||||
|
key = [self mj_replacedKeyFromPropertyName121:propertyName]; |
||||
|
} |
||||
|
|
||||
|
// 调用block |
||||
|
if (!key) { |
||||
|
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { |
||||
|
MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); |
||||
|
if (block) { |
||||
|
key = block(propertyName); |
||||
|
} |
||||
|
if (key) *stop = YES; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
// 查看有没有需要替换的key |
||||
|
if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) { |
||||
|
key = [self mj_replacedKeyFromPropertyName][propertyName]; |
||||
|
} |
||||
|
|
||||
|
if (!key || [key isEqual:propertyName]) { |
||||
|
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { |
||||
|
NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); |
||||
|
if (dict) { |
||||
|
key = dict[propertyName]; |
||||
|
} |
||||
|
if (key && ![key isEqual:propertyName]) *stop = YES; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
// 2.用属性名作为key |
||||
|
if (!key) key = propertyName; |
||||
|
|
||||
|
return key; |
||||
|
} |
||||
|
|
||||
|
+ (Class)mj_propertyObjectClassInArray:(NSString *)propertyName |
||||
|
{ |
||||
|
__block id clazz = nil; |
||||
|
if ([self respondsToSelector:@selector(mj_objectClassInArray)]) { |
||||
|
clazz = [self mj_objectClassInArray][propertyName]; |
||||
|
} |
||||
|
|
||||
|
if (!clazz) { |
||||
|
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { |
||||
|
NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); |
||||
|
if (dict) { |
||||
|
clazz = dict[propertyName]; |
||||
|
} |
||||
|
if (clazz) *stop = YES; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
// 如果是NSString类型 |
||||
|
if ([clazz isKindOfClass:[NSString class]]) { |
||||
|
clazz = NSClassFromString(clazz); |
||||
|
} |
||||
|
return clazz; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - --公共方法-- |
||||
|
+ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration |
||||
|
{ |
||||
|
// 获得成员变量 |
||||
|
NSArray *cachedProperties = [self mj_properties]; |
||||
|
// 遍历成员变量 |
||||
|
BOOL stop = NO; |
||||
|
for (MJProperty *property in cachedProperties) { |
||||
|
enumeration(property, &stop); |
||||
|
if (stop) break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 公共方法 |
||||
|
+ (NSArray *)mj_properties |
||||
|
{ |
||||
|
MJExtensionSemaphoreCreate |
||||
|
MJ_LOCK(mje_signalSemaphore); |
||||
|
NSMutableDictionary *cachedInfo = [self mj_propertyDictForKey:&MJCachedPropertiesKey]; |
||||
|
NSMutableArray *cachedProperties = cachedInfo[NSStringFromClass(self)]; |
||||
|
if (cachedProperties == nil) { |
||||
|
cachedProperties = [NSMutableArray array]; |
||||
|
|
||||
|
[self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { |
||||
|
// 1.获得所有的成员变量 |
||||
|
unsigned int outCount = 0; |
||||
|
objc_property_t *properties = class_copyPropertyList(c, &outCount); |
||||
|
|
||||
|
// 2.遍历每一个成员变量 |
||||
|
for (unsigned int i = 0; i<outCount; i++) { |
||||
|
MJProperty *property = [MJProperty cachedPropertyWithProperty:properties[i]]; |
||||
|
// 过滤掉Foundation框架类里面的属性 |
||||
|
if ([MJFoundation isClassFromFoundation:property.srcClass]) continue; |
||||
|
// 过滤掉`hash`, `superclass`, `description`, `debugDescription` |
||||
|
if ([MJFoundation isFromNSObjectProtocolProperty:property.name]) continue; |
||||
|
|
||||
|
property.srcClass = c; |
||||
|
[property setOriginKey:[self mj_propertyKey:property.name] forClass:self]; |
||||
|
[property setObjectClassInArray:[self mj_propertyObjectClassInArray:property.name] forClass:self]; |
||||
|
[cachedProperties addObject:property]; |
||||
|
} |
||||
|
|
||||
|
// 3.释放内存 |
||||
|
free(properties); |
||||
|
}]; |
||||
|
|
||||
|
cachedInfo[NSStringFromClass(self)] = cachedProperties; |
||||
|
} |
||||
|
NSArray *properties = [cachedProperties copy]; |
||||
|
MJ_UNLOCK(mje_signalSemaphore); |
||||
|
|
||||
|
return properties; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - 新值配置 |
||||
|
+ (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue { |
||||
|
MJExtensionSemaphoreCreate |
||||
|
MJ_LOCK(mje_signalSemaphore); |
||||
|
objc_setAssociatedObject(self, &MJNewValueFromOldValueKey, newValueFormOldValue, OBJC_ASSOCIATION_COPY_NONATOMIC); |
||||
|
MJ_UNLOCK(mje_signalSemaphore); |
||||
|
} |
||||
|
|
||||
|
+ (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(MJProperty *__unsafe_unretained)property{ |
||||
|
// 如果有实现方法 |
||||
|
if ([object respondsToSelector:@selector(mj_newValueFromOldValue:property:)]) { |
||||
|
return [object mj_newValueFromOldValue:oldValue property:property]; |
||||
|
} |
||||
|
|
||||
|
MJExtensionSemaphoreCreate |
||||
|
MJ_LOCK(mje_signalSemaphore); |
||||
|
// 查看静态设置 |
||||
|
__block id newValue = oldValue; |
||||
|
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { |
||||
|
MJNewValueFromOldValue block = objc_getAssociatedObject(c, &MJNewValueFromOldValueKey); |
||||
|
if (block) { |
||||
|
newValue = block(object, oldValue, property); |
||||
|
*stop = YES; |
||||
|
} |
||||
|
}]; |
||||
|
MJ_UNLOCK(mje_signalSemaphore); |
||||
|
return newValue; |
||||
|
} |
||||
|
|
||||
|
+ (void)mj_removeCachedProperties { |
||||
|
MJExtensionSemaphoreCreate |
||||
|
MJ_LOCK(mje_signalSemaphore); |
||||
|
[[self mj_propertyDictForKey:&MJCachedPropertiesKey] removeAllObjects]; |
||||
|
MJ_UNLOCK(mje_signalSemaphore); |
||||
|
} |
||||
|
|
||||
|
#pragma mark - array model class配置 |
||||
|
+ (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray |
||||
|
{ |
||||
|
[self mj_setupBlockReturnValue:objectClassInArray key:&MJObjectClassInArrayKey]; |
||||
|
|
||||
|
[self mj_removeCachedProperties]; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - key配置 |
||||
|
|
||||
|
+ (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName { |
||||
|
[self mj_setupBlockReturnValue:replacedKeyFromPropertyName key:&MJReplacedKeyFromPropertyNameKey]; |
||||
|
|
||||
|
[self mj_removeCachedProperties]; |
||||
|
} |
||||
|
|
||||
|
+ (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 { |
||||
|
MJExtensionSemaphoreCreate |
||||
|
MJ_LOCK(mje_signalSemaphore); |
||||
|
objc_setAssociatedObject(self, &MJReplacedKeyFromPropertyName121Key, replacedKeyFromPropertyName121, OBJC_ASSOCIATION_COPY_NONATOMIC); |
||||
|
|
||||
|
[[self mj_propertyDictForKey:&MJCachedPropertiesKey] removeAllObjects]; |
||||
|
MJ_UNLOCK(mje_signalSemaphore); |
||||
|
} |
||||
|
@end |
||||
|
#pragma clang diagnostic pop |
@ -0,0 +1,33 @@ |
|||||
|
//
|
||||
|
// NSString+MJExtension.h
|
||||
|
// MJExtensionExample
|
||||
|
//
|
||||
|
// Created by MJ Lee on 15/6/7.
|
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "MJExtensionConst.h" |
||||
|
|
||||
|
@interface NSString (MJExtension) |
||||
|
/**
|
||||
|
* 驼峰转下划线(loveYou -> love_you) |
||||
|
*/ |
||||
|
- (NSString *)mj_underlineFromCamel; |
||||
|
/**
|
||||
|
* 下划线转驼峰(love_you -> loveYou) |
||||
|
*/ |
||||
|
- (NSString *)mj_camelFromUnderline; |
||||
|
/**
|
||||
|
* 首字母变大写 |
||||
|
*/ |
||||
|
- (NSString *)mj_firstCharUpper; |
||||
|
/**
|
||||
|
* 首字母变小写 |
||||
|
*/ |
||||
|
- (NSString *)mj_firstCharLower; |
||||
|
|
||||
|
- (BOOL)mj_isPureInt; |
||||
|
|
||||
|
- (NSURL *)mj_url; |
||||
|
@end |
@ -0,0 +1,80 @@ |
|||||
|
// |
||||
|
// NSString+MJExtension.m |
||||
|
// MJExtensionExample |
||||
|
// |
||||
|
// Created by MJ Lee on 15/6/7. |
||||
|
// Copyright (c) 2015年 小码哥. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "NSString+MJExtension.h" |
||||
|
|
||||
|
@implementation NSString (MJExtension) |
||||
|
- (NSString *)mj_underlineFromCamel |
||||
|
{ |
||||
|
if (self.length == 0) return self; |
||||
|
NSMutableString *string = [NSMutableString string]; |
||||
|
for (NSUInteger i = 0; i<self.length; i++) { |
||||
|
unichar c = [self characterAtIndex:i]; |
||||
|
NSString *cString = [NSString stringWithFormat:@"%c", c]; |
||||
|
NSString *cStringLower = [cString lowercaseString]; |
||||
|
if ([cString isEqualToString:cStringLower]) { |
||||
|
[string appendString:cStringLower]; |
||||
|
} else { |
||||
|
[string appendString:@"_"]; |
||||
|
[string appendString:cStringLower]; |
||||
|
} |
||||
|
} |
||||
|
return string; |
||||
|
} |
||||
|
|
||||
|
- (NSString *)mj_camelFromUnderline |
||||
|
{ |
||||
|
if (self.length == 0) return self; |
||||
|
NSMutableString *string = [NSMutableString string]; |
||||
|
NSArray *cmps = [self componentsSeparatedByString:@"_"]; |
||||
|
for (NSUInteger i = 0; i<cmps.count; i++) { |
||||
|
NSString *cmp = cmps[i]; |
||||
|
if (i && cmp.length) { |
||||
|
[string appendString:[NSString stringWithFormat:@"%c", [cmp characterAtIndex:0]].uppercaseString]; |
||||
|
if (cmp.length >= 2) [string appendString:[cmp substringFromIndex:1]]; |
||||
|
} else { |
||||
|
[string appendString:cmp]; |
||||
|
} |
||||
|
} |
||||
|
return string; |
||||
|
} |
||||
|
|
||||
|
- (NSString *)mj_firstCharLower |
||||
|
{ |
||||
|
if (self.length == 0) return self; |
||||
|
NSMutableString *string = [NSMutableString string]; |
||||
|
[string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString]; |
||||
|
if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; |
||||
|
return string; |
||||
|
} |
||||
|
|
||||
|
- (NSString *)mj_firstCharUpper |
||||
|
{ |
||||
|
if (self.length == 0) return self; |
||||
|
NSMutableString *string = [NSMutableString string]; |
||||
|
[string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString]; |
||||
|
if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; |
||||
|
return string; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)mj_isPureInt |
||||
|
{ |
||||
|
NSScanner *scan = [NSScanner scannerWithString:self]; |
||||
|
int val; |
||||
|
return [scan scanInt:&val] && [scan isAtEnd]; |
||||
|
} |
||||
|
|
||||
|
- (NSURL *)mj_url |
||||
|
{ |
||||
|
// [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]]; |
||||
|
#pragma clang diagnostic push |
||||
|
#pragma clang diagnostic ignored"-Wdeprecated-declarations" |
||||
|
return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; |
||||
|
#pragma clang diagnostic pop |
||||
|
} |
||||
|
@end |
Loading…
Reference in new issue